目录
一and二、C语言说明
注释:
1、声明语句
2、输出函数
3、return 语句
三、C语言的数据结构
1、常量与变量
2、基本数据结构
3、关键字
练习:进制转换
四、基本输入输出
1、字符输出函数putchar
2、字符输入函数getchar
3、格式化输出函数printf
4、格式化输入函数scanf
5、字符串接收函数gets
6、字符串输出函数puts
练习:密码破译
五、运算符和表达式
1、赋值运算符“=”
2、算数运算符
3、sizeof运算符
4、逻辑运算符
5、?:条件运算符
6、关系运算符
7、表达式和语句
练习一:温度转换
练习二:求圆的面积
练习三、拆分位数
六、C语句和程序流
1、表达式和语句
2、if选择结构
3、switch case语句
4、break语句
5、while循环语句
6、do while循环语句
7、for循环语句
8、循环的嵌套
练习:九九乘法表
9、continue语句
练习一:分阶段函数求值
练习二:最大公约数和最小公倍数
练习三:水仙花数判断
练习四:阶乘求和
练习五:筛选N以内的素数
练习六:Sn的公式求和
七、C语言函数
1、函数的定义
2、函数的调用
3、变量的存储类型
4、auto自动变量类型
5、extern外部变量
6、static静态变量
7、register寄存器变量
练习一:数字逆序输出
练习二:自定义函数求一元二次方程
练习三:自定义函数处理素数
练习四:8除不尽的数
练习五:大、小写问题
练习六:求1!+2!+……+N!的值
八、数组
1、一维数组
2、二维数组
3、字符数组和字符串
(1)strcpy()函数:拷贝一个字符串到另一个字符串数组中
(2)strcat()函数:将一个字符串拼接在目标字符串的后面
(3)strcmp()函数:比较两个字符串的大小
(4)strchr()函数:查找字符串中第一个出现的指定字符的位置
(5)strcmpi()函数:不区分大小写,比较两个字符串的大小
练习一:选择排序
练习二:数字逆序输出
练习三:分段函数
练习四:求偶数和
练习五:统计字符
练习六:阶乘数列
九、指针
1、程序中地址与指针
2、指针的定义和使用
3、数组与指针的区别与联系
4、字符串与指针的用法
5、常用字符串处理函数
很早之前的笔记,跨度较久,因为篇幅太长所以分成多个文章。
没什么参考性,只为了个人后续方便查询基础知识。
C语言入门教程(配套编程题库) - C语言网
一and二、C语言说明
C语言的特点:高效性、可移植性、灵活性、面向编程人员
main函数开始,到main函数结束而停止。
int main()中,int指明main()函数返回给操作系统的函数返回值的基本类型;圆括号一般包含传递给函数的信息
注释:
- 快捷键: 1)Ctrl+K+C,即可实现//注释(选中多行也能注释)。 2)Ctrl+K+U,取消//注释(多行也可以)。
- 多行注释:/* (可多行,多行行首不加额外符号)*/
- 单行注释://
1、声明语句
- 在首次使用变量之前仍然必须先声明它;
- int num;说明函数有一个名为num的变量;说明num是一个整数;
2、输出函数
- printf ("Hello!");
- printf ("My birthday is %d\n", num);
3、return 语句
- return语句可以看作是保持逻辑连贯性所需的内容;
三、C语言的数据结构
1、常量与变量
- 常量是指程序在运行时其值不能改变的量。常量不占内存,在程序运行时它作为操作对象直接出现在运算器的各种寄存器中。
- 变量是指在程序运行时其值可以改变的量。变量的功能就是存储数据。
- 变量的定义:在程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定义外,其余都由用户自定义。
- 标识符:(1)用于标识某个量的符号;(2)在标识符中,大小写是有区别的。
2、基本数据结构
- 按其在计算机中的存储方式可被分为两个系列,即整数(integer)类型和浮点数(floating-point)类型;
- 分别是:short、int、long、char、float、double这六个关键字再加上两个符号说明符signed和unsigned就基本表示了C语言的最常用的数据类型;
类型名称 | 类型关键字 | 占字节数 | 其他叫法 | 表示的数据范围 |
字符型 | char | 1 | signed char | -128 ~ 127 |
无符号字符型 | unsigned char | 1 | none | 0 ~ 255 |
整型 | int | 4 | signed int | -2,147,483,648 ~ 2,147,483,647 |
无符号整型 | unsigned int | 4 | unsigned | 0 ~ 4,294,967,295 |
短整型 | short | 2 | short int | -32,768 ~ 32,767 |
无符号短整型 | unsigned short | 2 | unsigned short int | 0 ~ 65,535 |
长整型 | long | 4 | long int | -2,147,483,648 ~ 2,147,483,647 |
无符号长整型 | unsigned long | 4 | unsigned long | 0 ~ 4,294,967,295 |
单精度浮点数 | float | 4 | none | 3.4E +/- 38 (7 digits) |
双精度浮点数 | double | 8 | none | 1.7E +/- 308 (15 digits) |
长双精度浮点数 | long double | 10 | none | 1.2E +/- 4932 (19 digits) |
长整型 | long long | 8 | __int64 | -9223372036854775808~9223372036854775808 |
3、关键字
用户自己定义的变量函数名等要注意不可以与关键字同名。
C语言中的32个关键字 | |||
auto | double | int | struct |
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
const | float | short | unsigned |
continue | for | signed | void |
default | goto | sizeof | volatile |
do | if | static | while |
练习:进制转换
#include<stdio.h>
int main()
{
int a;
scanf("%d", &a);
printf("%#o %d %#x", a, a, a);
return 0;
}
四、基本输入输出
1、字符输出函数putchar
如果输出成功返回一个字符的ASC码,失败则返回EOF即-1,如代码:
putchar(‘A’); /*输出大写字母A */
putchar(x); /*输出字符变量x的值*/
putchar(‘\n’); /*换行*/
这个输出的时候只能输出单字符,如果是字符串的话就会无任何输出。
2、字符输入函数getchar
getchar会以返回值的形式返回接收到的字符。即该字符的ASC码,通常的用法如下:
char c; /*定义字符变量c*/
c=getchar(); /*将读取的字符赋值给字符变量c*/
3、格式化输出函数printf
按照用户指定的格式,把指定的数据输出到屏幕上
printf("格式控制字符串",输出表项);
- 格式控制字符串用来说明输出表项中各输出项的输出格式;
- 输出表项列出了要输出的项,各输出项之间用逗号分开。
格式控制字符串有两种:格式字符串和非格式字符串。非格式字符串在输出的时候原样打印;格式字符串是以%打头的字符串,在“%”后面跟不同格式字符,用来说明输出数据的类型、形式、长度、小数位数等。格式字符串的形式为:
% [输出最小宽度] [.精度] [长度] 类型
例如:%d格式符表示用十进制整形格式输出,%f表示用实型格式输出,%5.2f格式表示输出宽度为5(包括小数点),并包含2位小数。
常用的输出格式及含义如下:
格式字符 | |
d , i | 以十进制形式输出有符号整数(正数不输出符号) |
O | 以八进制形式输出无符号整数(不输出前缀0) |
x | 以十六进制形式输出无符号整数(不输出前缀0x) |
U | 以十进制形式输出无符号整数 |
f | 以小数形式输出单、双精度类型实数 |
e | 以指数形式输出单、双精度实数 |
g | 以%f或%e中较短输出宽度的一种格式输出单、双精度实数 |
c | 输出单个字符 |
s | 输出字符串 |
#include<stdio.h>
int main()
{
char a='c';
float m=8.55555555;
unsigned width;
scanf("%d",&width);
int height=365;
printf("%c\n",a);
printf("%3.2f\n",m);
printf("this apple height id %d and width is %d\n",height,width);
}
printf的返返回值:返回打印的字符书目,如果输出错误的话,可能会返回一个负数。
4、格式化输入函数scanf
scanf函数称为格式输入函数,即按照格式字符串的格式,从键盘上把数据输入到指定的变量之中。
scanf("格式控制字符串",输入项地址列表);
格式控制字符串的作用与printf函数相同,但不能显示非格式字符串,也就是不能显示提示字符串。地址表项中的地址给出各变量的地址,地址是由地址运算符“&”后跟变量名组成的【地址运算符&不可少】。
- (1)格式说明符中,可以指定数据的宽度,但不能指定数据的精度。
- (2)输入long类型数据时必须使用%ld,输入double数据必须使用%lf或%le。
- (3)附加格式说明符“*”使对应的输入数据不赋给相应的变量。
转换说明符 | |
%c | 把输入解释成一个字符 |
%d | 把输入解释成一个有符号十进制整数 |
%e,%f,%g,%a | 把输入解释成一个float浮点数(%a是C99的标准) |
%E,%F,%G,%A | 把输入解释成一个浮点数(%A是C99的标准) |
%i | 把输入解释成一个有符号十进制整数 |
%o | 把输入解释成一个有符号的八进制整数 |
%p | 把输入解释成一个指针(一个地址) |
%s | 把输入解释成一个字符串:输入的内容以第一个非空白字符作为开始,并且包含直到下一个空白字符的全部字符 |
%u | 把输入解释成一个无符号十进制整数 |
%x,%X | 把输入解释称一个有符号十六进制整数 |
scanf的返回值:返回的是读入项目的个数。
float a;
scanf("%f",&a);
int b;
scanf("%d",&b);
double c;
scanf("%lf",&c);
printf("a=%0.2f, b=%d, c=%0.3lf",a,b,c);
输入:
1.22222
2
3.55555输出:
a=1.22, b=2, c=3.556
5、字符串接收函数gets
函数原型:
char *gets(char *str);
可以用gets直接往字符数组里接收字符串.函数原型传入一个字符指针,也就是存放这个字符串的字符数组地址即可接收。
当执行gets函数时,程序会等待用户从键盘输入的字符串,一旦遇到结束标志,即回车,那么之前输入在缓冲区中的字符串则会一起输入到gets中str所指向的内存区域里。
scanf接收时的结束标志有空格和回车,而gets不包括空格。
#include<stdio.h>
int main(void)
{
char str[100]="\0";
gets(str);
printf("%s\n",str);
return 0;
}
6、字符串输出函数puts
C语言中的字符串输出函数除了printf还可以用puts。
int puts(const char *s);
在包含头文件# include <stdio.h>后,即可将想要输出的字符串或者字符数组的地址传入puts即可打印输出。
#include<stdio.h>
int main(void)
{
char str[10]="asdfgg";
printf("%s\n",str);
puts(str);
}
这样的输出就是一致的。
练习:密码破译
#include<stdio.h>
int main(void)
{
char s[11];
gets(s);
for(int i=0;s[i]!='\0';i++){
if(s[i]>='A'&&s[i]<='Z'){
s[i]=(s[i]+4-'A')%26+'A';
}
if(s[i]>='a'&&s[i]<='z'){
s[i]=(s[i]+4-'a')%26+'a';
}
}
puts(s);
return 0;
}
INPUT:China
OUTPUT:Glmre
五、运算符和表达式
1、赋值运算符“=”
赋值运算符“=”,顺序是从右到左,例如num=2014;【读作“将2014赋值给num”】
2014=num;这种就是错误的,因为不能将一个值赋给一个常量;
几个术语:数据对象、左值、右值和操作数。
- “数据对象”(data object)是泛指数据存储区的术语,数据存储区能用于保存值。例如:用于保存变量或数组的数据存储区是一个数据对象。
- 左值(lvalue)指用于标识一个特定的数据对象的名字或表达式。例如:变量的名字是一个左值。所以对象指的是实际的数据存储,但是左值是用于识别或定位那个存储的标识符。
- 右值(rvalue)指的是能赋给可修改的左值的量。
- num=2014;就是一个可修改的左值,2014是一个右值。
2、算数运算符
算数运算符主要包括:加法+、减法-、乘法*、除法/、求模%、自增++、自减--。
自增++和自减--,是单目运算符,因为它们只需要一个操作数,加法+、减法-、乘法*、除法/、求模%,是双目运算符。
对于自增++和自减--有两种模式:
- 前缀运算符(++i),先执行自增或自减运算,再计算表达式的值;
- 后缀运算符(i++),则先计算表达式的值,再执行自增或自减运算。
运算符 | 符号 | 操作 | 用例 | 结果 |
加法 | + | 使它两侧的值加到一起 | 1+2 | 3 |
减法 | - | 从它前面的数减去后面的数 | 5-3 | 2 |
乘法 | * | 将它前面的数乘以后面的数 | 2*3 | 6 |
除法 | / | 用它左边的值除以右边的值 | 8/3 | 2(整数的除法会丢掉小数部分) |
取模 | % | 求用它左边的值除以右边的数后的余数 | 5%3 | 2(%运算符两侧的操作数必须为整数) |
3、sizeof运算符
长度(求字节)运算符,sizeof是一种单目运算符,以字节为单位返回某操作数的大小,用来求某一类型变量的长度。【int通常占用4个字节(32位)】
#include<stdio.h>
int main()
{
int n=0;
int intsize = sizeof(int);
printf("int sizeof is %d bytes\n",intsize);
return 0;
}
4、逻辑运算符
C语言中逻辑运算符包含逻辑与&&、逻辑或||、逻辑非!(单目)三种。
int a=a,b=2;
unsigned c=a&&b;
printf("%d",c);
像这种就会输出为输出为1,因为判断过程是判断两边是否为真(即≠0),同时输出的是数字。
5、?:条件运算符
表达式1?表达式2:表达式3
根据表达式1判断三目运算符整体的值:
- 表达式1成立,整体的值就是表达式2的值;
- 否则,整体的值就是表达式3的值。
#include<stdio.h>
int main()
{
int a=5,b=6,c=7;
int m=a?(b*c):(b-c);
printf("%d\n",m);
printf("%d",&m);
}
输出为:
42
366303764
之所以打印结果不同,是因为:&
运算符用于获取变量的地址
输出正常的bool类型:
bool m = a ? true : false;
printf("%s", m ? "true" : "false");
6、关系运算符
分别有:大于>、小于<、大于或等于>=、小于或等于<=、是否等于==、是否不等于!=共六种。
双目运算,比较结果是逻辑值(非0即1)。
7、表达式和语句
(1)表达式(expression)是由运算符和操作数组合构成的(操作数是运算符操作的对象)。
操作数可以是常量,也可以是变量,亦可以是他们的组合。一些表达式是多个较小的表达式的组合,这些小的表达式称为子表达式(subexpression)。
(2)语句是构造程序的基本部分。程序(program)是一系列带有某种必须的标点语句集合。一个语句是一条完整的计算机指令。在C中,语句用结束处的一个分号标示。
(3)表达式后面加一个分号即构成了一条C语句(它们被称为表达式语句)
例如:C=3是表达式,那么C=3;就是表达式语句。
练习一:温度转换
#include<stdio.h>
int main()
{
double F,c;
scanf("%lf",&F);
c=5*(F-32)/9;
printf("c=%0.2lf",c);
return 0;
}
练习二:求圆的面积
输入半径,输出面积
#include<stdio.h>
int main()
{
float a,b;
scanf("%f",&a);
b=3.1415926*a*a;
printf("%0.2f",b);
}
练习三、拆分位数
#include<stdio.h>
int main()
{
int a,b,c,d;
scanf("%d",&a);
b=a/100;
c=(a%100)/10;
d=(a%100)%10;
printf("%d %d %d",d,c,b);
}
六、C语句和程序流
1、表达式和语句
表达式代表值,而语句代表给计算机的指令。
- 表达式由运算符和操作数组成。最简单的表达式只是一个不带运算符的常量或者变量,例如12或者num。复杂一些的例子是20+30和a=12。
- 语句是对计算机的命令。任何以分号结尾的表达式都是一个语句,它不一定要有意义。语句可以是简单语句或复合语句。简单语句(simple statement)以分号结尾,复合语句(compound statement)或代码块(block)由用花括号括起来的一个或多个语句(它们本身也可以是复合语句)组成。
2、if选择结构
if else选择程序结构用于判断给定的条件,根据判断条件的成立与否来控制程序的流程。选择结构有单选择、双选择和多选择3种形式,单选择结构用if语句实现。
(1)形式一:
if
(表达式)
/*若条件成立则实行花括号里的语句,反之则不执行*/
{
//语句
}
(2)形式二:
if
(表达式)
/*若表达式成立则执行语句1,否则执行语句2*/
{
//语句1
}
else
{
//语句2
}
(3)形式三:
if
(表达式)
/*如果表达式成立,执行语句1否则继续判断表达式2*/
{
//语句1
}
else
if
(表达式2)
/*如果表达式成立,执行语句2否则继续判断表达式3*/
{
//语句2
}
else
if
(表达式3)
/*如果表达式成立,则执行语句3否则继续判断下一个表达式*/
{
//语句3;
}
//… …
else
/*如果以上表达式都不成立 则执行语句4*/
{
//语句4
}
3、switch case语句
除了用多分支选择结构else if之外,C语言还提供了switch的结构。
switch语句的执行过程为:首先计算表达式的值,然后依次与常量表达式依次进行比较,若表达式的值与某常量表达式相等,则从该常量表达式处开始执行,直到switch语句结束。若所有的常量表达式的值均不等于表达式的值,则从default处开始执行。
switch
(表达式)
/*首先计算表达式的值*/
{
case
常量表达式1:语句1;
case
常量表达式2:语句2;
case
常量表达式3:语句3;
// ……
case
常量表达式n:语句n;
default
:语句n+1;
}
switch中的每个case只能是和case后的常量表达式比较是否相等,但是else if中的条件既可以相等也可以满足某个区间。
#include<stdio.h>
int main()
{
int b;
scanf("%d",&b);
switch(b){
case 50:printf("bad!\n");
case 60:printf("well!\n");
default:printf("None!\n");
}
}
实验结果:
输入:50
输出:
bad!
well!
None!
因为没有break语句,所以会先判断从哪个case执行,然后按照顺序执行后面所有case的代码块。
4、break语句
break语句,仅用于跳出switch结构或循环结构,用于提前结束switch结构或循环。
switch
(表达式)
/*首先计算表达式的值*/
{
case
常量表达式1:语句1;break;
case
常量表达式2:语句2;break;
case
常量表达式3:语句3;break;
// ……
case
常量表达式n:语句n;break;
default:语句n+1;break;
}
#include<stdio.h>
int main()
{
int b;
scanf("%d",&b);
switch(b){
case 50:printf("bad!\n");break;
case 60:printf("well!\n");break;
default:printf("None!\n");break;
}
}
这样就会正常输出一个bad!
5、while循环语句
C语言提供三种循环结构,分别为while循环、do while循环和for循环。
while语句创建一个循环,该循环在判断表达式为假(或0)之前重复执行。while语句是一个入口条件(entry-condition)循环,在进行一次循环之前决定是否要执行循环。因此有可能一次也不执行。循环的语句部分可以是一个简单语句或一个复合语句。
while
(表达式)
{
循环体语句
}
在表达式为假(或0)之前重复执行循环体语句部分。
6、do while循环语句
do while语句创建一个循环,它在判断表达式为假(或0)之前重复执行。do while语句是一个退出条件循环,在执行一次循环之后才决定是否要再次执行循环,因此循环至少要被执行一次。循环的语句部分可以是一个简单语句或一个复合语句。
do
{
循环体语句
}
while
(表达式);
在表达式为假(或0)之前重复执行循环体语句。
7、for循环语句
for语句使用由分号隔开的三个控制表达式来控制循环过程。初始化表达式只在开始执行循环语句之前执行一次。如果判断表达式为真(或非0)就执行一次循环。然后计算更新表达式并再次检查判断表达式的值。for语句是一个入口条件循环,在进行一次循环之前决定是否要执行循环,因此有可能循环一次也不执行。循环的语句部分可以是一个简单语句或一个复合语句。
for
(初始化表达式;判断表达式;更新表达式)
{
循环体语句
}
循环在判断表达式为假(或0)之前重复执行。
int main()
{
for(int i=0;i<4;i++){
printf("%d\n",i);
}
return 0;
}
更新表达式在最后。
循环的三大条件:初始值,增量,循环条件。
for循环可以直接替代前面的while和do while循环。
8、循环的嵌套
循环的嵌套,即循环中还有循环。
int main()
{
for(int i=0;i<4;i++){
for(int j=0;j<6;j++){
printf("%d\n",i*j);
}
}
return 0;
}
外部循环执行一次,内部循环执行一轮【内部的循环全部执行完(即j从0执行到10),外部的循环才会执行一次(即i会增加1)】。
练习:九九乘法表
#include<stdio.h>
int main()
{
for(int i=1;i<10;i++){
for(int j=1;j<=i;j++){
int m=j*i;
printf("%d*%d=%-3d",j,i,m);
}
printf("\n");
}
return 0;
}
%-3d
:-
是一个标志,表示左对齐输出。3
是指定的最小字段宽度,表示要打印的整数至少占用3个字符的宽度(包括数字和可能的符号)。
9、continue语句
仅用于循环中,用于提前结束本次循环,即跨过continue后面的循环语句,提前进入下次循环【break在switch和循环中都可以使用】。
练习一:分阶段函数求值
有一个函数
y={ x x<1
| 2x-1 1<=x<10
{ 3x-11 x>=10写一段程序,输入x,输出y
#include<stdio.h>
int main()
{
int x,y;
scanf("%d",&x);
if(x<1){
y=x;
}else if(x>=1&&x<10){
y=2*x-1;
}else{
y=3*x-11;
}
printf("%d",y);
return 0;
}
练习二:最大公约数和最小公倍数
输入两个正整数m和n,求其最大公约数和最小公倍数。
思想:定义一个最大公约数的初始值,找到一个两个数都能整除的数,然后赋值给最大公约数。最小公倍数是利用两个数的乘积除以最大公约数。
#include<stdio.h>
int main()
{
int m,n,max,min,k=1;
scanf("%d %d",&m,&n);
while(k<=m&&k<=n){
if(m%k==0&&n%k==0){
max=k;
}
k++;
}
min=(m*n)/max;
printf("%d %d",max,min);
return 0;
}
练习三:水仙花数判断
打印出所有"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该本身。 例如:153是一个水仙花数,因为153=1^3+5^3+3^3。
#include<stdio.h>
int main()
{
int m=100,b,s,g;
while(m>99&&m<1000){
b=m/100;
s=(m%100)/10;
g=m%10;
if(m==(b*b*b+s*s*s+g*g*g)){
printf("%d\n",m);
}
m++;
}
return 0;
}
练习四:阶乘求和
求Sn=1!+2!+3!+4!+5!+…+n!之值,其中n是一个数字(n不超过20)。
#include<stdio.h>
int main()
{
int n;
long long Sn=0,i;
scanf("%d",&n);
for(int j=1;j<=n;j++){
i=1;
for(int m=0;m<j;m++){
i*=(m+1);
}
Sn=Sn+i;
}
printf("%lld",Sn);
return 0;
}
练习五:筛选N以内的素数
用简单素数筛选法求N以内的素数。
有一个最后判断的过程,一定不能判断错误。应该是内层循环后再判断。
#include<stdio.h>
int main()
{
int n,a;
scanf("%d",&n);
for(int i=2;i<=n;i++){
a=0;
for(int j=2;j<i;j++){
if(i%j==0){
a++;
}
}
if(a==0){
printf("%d\n",i);
}
}
return 0;
}
练习六:Sn的公式求和
求Sn=a+aa+aaa+…+aa…aaa(有n个a)之值,其中a是一个数字,为2。 例如,n=5时=2+22+222+2222+22222,n由键盘输入。
【思想】:这个还是采用归纳法解决的,有其他更简单的思想,但是写起来比较麻烦。
#include<stdio.h>
int main()
{
int n,sn=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
sn *= 10;
sn += 2*(i+1);
}
printf("%d",sn);
return 0;
}
七、C语言函数
1、函数的定义
C源程序是由函数组成的。最简单的程序有一个主函数main(),但实用程序往往由多个函数组成,由主函数调用其他函数,其他函数也可以互相调用。
函数是C源程序的基本模块,程序的许多功能是通过对函数模块的调用来实现的,学会编写和调用函数可以提高编程效率。
函数的定义:
返回值类型 函数名(形参表说明)
/*函数首部*/
{
说明语句
/*函数体*/
执行语句
}
对函数定义的说明:
- “返回值类型”是指函数返回值的类型。函数返回值不能是数组,也不能是函数,除此之外任何合法的数据类型都可以是函数的类型,如:int,long,float,char等。函数类型可以省略,当不指明函数类型时,系统默认的是整型。
- 函数名是用户自定义的标识符,在C语言函数定义中不可省略,须符合C语言对标识符的规范,用于标识函数,并用该标识符调用函数。另外函数名本身也有值,它代表了该函数的入口地址,使用指针调用函数时,将用到此功能。
- 形参又称为“形式参数”。形参表是用逗号分隔的一组变量说明,包括形参的类型和形参的标识符,其作用是指出每一个形参的类型和形参的名称,当调用函数时,接收来自主调函数的数据,确定各参数的值。(即如果要调用这个函数,就要给形参赋值)
- 用{ }括起来的部分是函数的主体,称为函数体。函数体是一段程序,确定该函数应完成的规定的运算,应执行的规定的动作,集中体现了函数的功能。函数内部应有自己的说明语句和执行语句,但函数内定义的变量不可以与形参同名。花括号{ }是不可以省略的。
int add()
{
returrn 0;
}
像这种空函数可以用来标记暂时未开发完毕的函数。
#include<stdio.h>
int max(int x,int y){
if(x>y){
return(x);
}else{
return(y);
}
}
int main()
{
auto int a,b,c;
scanf("%d %d",&a,&b);
c=max(a,b);
printf("%d",c);
return 0;
}
在判断大小的时候,一定要写return(x),不然就不会返回任何值了。
2、函数的调用
主调函数使用被调函数的功能,称为函数调用。
只有在函数调用时,函数体中定义的功能才会被执行。
函数调用的一般形式为:
函数名(类型 形参,类型 形参...);
对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数、变量或其他构造类型数据及表达式,各实参之间用逗号分隔。
调用方式:
(1)函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的,例如:
z=max(x,y);
(2)函数语句:函数调用的一般形式加上分号即构成函数语句,例如:
printf("%d",a);
scanf("%d",&b);
以函数语句的方式调用函数。
(3)函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的,例如:
printf("%d",max(x,y));/*把max调用的返回值作为printf函数的实参*/
在主调函数中调用某函数之前应对该被调函数进行声明,在主调函数中对被调函数进行声明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值进行相应的处理。
类型说明符 被调函数名(类型 形参,类型 形参...);
函数的声明和函数的定义有本质上的不同,主要区别在以下两个方面:
- 函数的定义是编写一段程序,应有函数的具体功能语句——函数体,而函数的声明仅是向编译系统的一个说明,不含具体的执行动作。
- 在程序中,函数的定义只能有一次,而函数的声明可以有多次。
3、变量的存储类型
变量是对程序中数据所占内存空间的一种抽象定义,定义变量时,用户定义变量的名、变量的类型,这些都是变量的操作属性。不仅可以通过变量名访问该变量,系统还通过该标识符确定变量在内存中的位置。
保存变量当前值的存储单元有两类,一类是内存,另一类是CPU的寄存器。变量的存储类型关系到变量的存储位置,C语言中定义了4种存储属性,即自动变量(auto)、外部变量(extern)、静态变量(static)和寄存器变量(register),它关系到变量在内存中的存放位置,由此决定了变量的保留时间和变量的作用范围。
变量的保留时间又称为生存期,从时间角度,可将变量分为静态存储和动态存储两种情况:
(1)静态存储是指变量存储在内存的静态存储区,在编译时就分配了存储空间,在整个程序的运行期间,该变量占有固定的存储单元,程序结束后,这部分空间才释放,变量的值在整个程序中始终存在。
(2)动态存储是指变量存储在内存的动态存储区,在程序的运行过程中,只有当变量所在的函数被调用时,编译系统才临时为该变量分配一段内存单元,函数调用结束,该变量空间释放,变量的值只在函数调用期存在。
变量的作用范围又称为作用域,从空间角度,可以将变量分为全局变量和局部变量:
(1)局部变量是在一个函数或复合语句内定义的变量,它仅在函数或复合语句内有效,编译时,编译系统不为局部变量分配内存单元,而是在程序运行过程中,当局部变量所在的函数被调用时,编译系统根据需要,临时分配内存,调用结束,空间释放。
(2)全局变量是在函数之外定义的变量,其作用范围为从定义处开始到本文件结束,编译时,编译系统为其分配固定的内存单元,在程序运行的自始至终都占用固定单元。
4、auto自动变量类型
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间,这类局部变量称为自动变量。
自动变量用关键字auto进行存储类别的声明,例如声明一个自动变量:
int fun(int a)
{
auto int b,c=3;/*定义b,c为自动变量*/
}
a是函数fun()的形参,b、c是自动变量,并对c赋初值3。执行完fun()函数后,自动释放a、b、c所占的存储单元。
5、extern外部变量
外部变量,即全局变量,是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。
如果外部变量不在文件的开头进行定义的话,那么有效的作用范围只限于定义处到文件尾部。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量进行“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
通常的,用extern声明外部变量,扩展程序文件中的作用域。
6、static静态变量
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为静态局部变量,用关键字static进行声明。
#include<stdio.h>
static x=80;
int func(){
x=x+1;
printf("a=%d\n",x);
return 0;
}
int main()
{
for(int i=0;i<10;i++){
func();
}
return 0;
}
如果按照这样的代码方法,就是输出的是从81-89这些数字,相当于函数值是一直保存的,所以再次调用的时候使用的是上次的函数值。
#include<stdio.h>
int func(){
int x=80;
x=x+1;
printf("a=%d\n",x);
return 0;
}
int main()
{
for(int i=0;i<10;i++){
func();
}
return 0;
}
如果定义成这种,在每次函数调用的时候,都会使用初始值80,因此应该是输出9遍81。
7、register寄存器变量
C语言允许将局部变量的值存放在CPU的寄存器中,这种变量叫做寄存器变量,用关键字register声明。使用寄存器变量需要注意以下几点:
- (1)只有局部自动变量和形式参数可以作为寄存器变量。
- (2)一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。
- (3)不能使用取地址运算符“&”求寄存器变量的地址。
register int a=0;//将变量a存储在寄存器上
【注】:形式参数(formal parameters)是函数定义中声明的参数,用于接收函数调用时传递的实际参数(actual arguments)的值。
练习一:数字逆序输出
练习二:自定义函数求一元二次方程
练习三:自定义函数处理素数
练习四:8除不尽的数
练习五:大、小写问题
练习六:求1!+2!+……+N!的值
八、数组
1、一维数组
数组是同类型有序数据的集合,这些数据集合的名字叫做数组名。数组中的各个数据项称为数组元素。
其中数组元素可以用数组名和下标表示【数组下标从0开始表示】。
一维数组的定义:
类型说明符 数组名 [常量表达式];
int
a[100];
//定义一个数组名为a,存储100个int类型的数组,其元素分别是a[0]~a[99]
float
b[10];
//数组名为b的,存储10个float类型的数组,其元素分别是b[0]~b[9]
如果只定义数组的话,数组里面的值都是未初始化的。也可以在定义的时候初始化赋值。
如果给部分元素赋初值,未经赋值的元素会自动赋值为0。
int类型未被赋值的元素为0,浮点型为小数类型,而字符类型则为'\0'。
int
a[100]={1,2,3,4,5};
//定义一个整型数组a,前5个元素即赋值为1,2,3,4,5,后95个元素值值全部为0
float
b[10]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,0.0};
//定义float数组b并对全部float类型的元素都分别赋值
2、二维数组
二维数组的定义:
类型说明符 数组名[行数][列数];
例如:
int
a[3][4];
/*定义一个整形二维数组a,有3行4列共12个元素分别为:
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
*/
char
arry[10][10];
//定义一个字符型二维数组arry,有10行10列,依次为arry[0][0]~arry[9][9]共100个元素
如果初始化赋值的话:
int
a[3][4]={{1,2,3,4},{10,20,30,40},{100,200,300,400}};
//定义一个三行四列的二维数组,按行赋值
int
a[3][4]={1,2,3,4,10,20,30,40,100,200,300,400};
//定义一个三行四列的二维数组并对其中的12(3*4)个元素进行赋值
【二维数组与一维数组一样在内存中的存储也是按照线性排布的,所以定义int a [3][4]和定义a[12]差不多】
#include<stdio.h>
int main()
{
int a[12]={1,2};
for(int i=0;i<12;i++){
printf("%d\n",a[i]);
}
return 0;
}
输出数组需要逐个输出。
3、字符数组和字符串
用来存放字符的数组称为字符数组。字符数组的各个元素依次存放字符串的各字符,字符数组的数组名代表该数组的首地址。
字符数组的定义:
char
c[10];
字符数组的初始化:
char
c[6]={
'c'
,
' h '
,
'i'
,
'n'
,
'a'
,
'\0'
};
其中,‘\0’为字符串结束符。如果不对c[5]赋任何值,‘\0’会由系统自动添加。字符数组也可采用字符串常量的赋值方式,例如:
char a[]={"china"};
在输出字符和字符串的候:
%c | 输出单个字符 |
% | 输出字符串 |
#include<stdio.h>
int main()
{
char a[3]={'a','b','\0'};
for(int i=0;i<3;i++){
printf("%c\n",a[i]);
}
return 0;
}
(1)strcpy()函数:拷贝一个字符串到另一个字符串数组中
函数名: strcpy
头文件: <string.h>
函数原型: char *strcpy(char *destin, const char *source);
功 能: 拷贝一个字符串到另一个字符串数组中
参数: char *destin 为复制的目标字符串数组
const char *source 为复制的源字符串数组
返回值: 返回指向目标字符串数组的指针
注意:必须保证 destin 足够大,能够容纳下 source,否则会导致溢出错误。该函数不会生成新字符串,而是修改原有字符串。因此destin只能是字符数组,而不能是字符串指针指向的字符串,因为字符串指针指向的是字符串常量, 常量不能被修改。
#include<stdio.h>
#include<string.h>
int main(void){
char string1[10];
char *string2="better!";
strcpy(string1,string2);
printf("%s\n",string1);
return 0;
}
输出:better!
如果string1的空间不足的话,会提示:
(2)strcat()函数:将一个字符串拼接在目标字符串的后面
函数名: strcat
头文件: <string.h>
功 能: 将一个字符串拼接在目标字符串的后面
函数原型: char *strcat(char *destin, const char *source);
功 能: 将一个字符串拼接在目标字符串的后面
参数: char *destin 为目标字符串数组
const char *source 为要拼接的字符串数组
返回值:返回拼接成功后的字符串数组的指针
注意:必须保证 destin 足够大,能够容纳下 source,否则会导致溢出错误。该函数不会生成新字符串,而是修改原有字符串。因此destin只能是字符数组,而不能是字符串指针指向的字符串,因为字符串指针指向的是字符串常量, 常量不能被修改。
其中char *string2 = "xxxxxxxxxbetter!"; 和 char string2[] = {"xxxxxxxxxbetter!"}的区别是:前者生成一个不可更改的字符串常量,后者是一个可修改的字符串。
#include<stdio.h>
#include<string.h>
int main(void){
char string1[30]={"China will be"};
char *blank=" ", *string2="better!";
strcat(string1,blank);
strcat(string1,string2);
printf("%s\n",string1);
return 0;
}
输出:China will be better!
(3)strcmp()函数:比较两个字符串的大小
函数名: strcmp
头文件: <string.h>
函数原型: int strcmp(const char *str1,const char *str2);
功 能: 比较两个字符串的大小,区分大小写
参 数: str1和str2为要比较的字符串
返回值: str1 > str2 , 返回 1;
str1 < str2 , 返回 -1;
str1 == str2 , 返回 0;
#include<stdio.h>
#include<string.h>
int main(void){
char string1[30]={"China will be"};
char *string2="better!";
int ptr=strcmp(string1,string2);
if(ptr>0){
printf("1");
}else if(ptr<0){
printf("0");
}else{
printf("=");
}
return 0;
}
(4)strchr()函数:查找字符串中第一个出现的指定字符的位置
函数名: strchr
头文件:<string.h>
函数原型: char *strchr(const char *str, char c);
功能: 查找字符串中第一个出现的指定字符的位置
参数: char *str 为要查找的目标字符串;
char c 为要查找的字符;
返回值: 成功 返回字符第一次出现的位置;失败 返回NULL;
#include<stdio.h>
#include<string.h>
int main(void){
char string1[30]={"China will be"};
char *ptr,c='m';
ptr = strchr(string1,c);
if(ptr){
printf("%c的位置是%d\n",c,ptr-string1);
}else{
printf("NULL");
}
return 0;
}
输出:NULL
(5)strcmpi()函数:不区分大小写,比较两个字符串的大小
函数名: strcmpi
头文件:<string.h>
函数原型: int strcmpi(char *str1, char *str2);
功能:比较两个字符串的大小,但是不区分大小写
参数:str1和str2为要比较的字符串
返回值:str1>str2 返回1;
str1==str2 返回0;
str1<str2 返回-1;
练习一:选择排序
本章总结与作业 - C语言教程 - C语言网
练习二:数字逆序输出
练习三:分段函数
练习四:求偶数和
练习五:统计字符
练习六:阶乘数列
九、指针
1、程序中地址与指针
内存(虚拟的逻辑内存空间)中标识某个点的编号。
在我们最广泛使用的32位操作系统下,是从0~4,294,967,295之间,而地址就是这之中的的一个编号而已。
习惯上,在计算机里地址我们常常用其对应的十六进制数来表示,比如0x12ff7c。
C程序中,每一个定义的变量,在内存中都占有一个内存单元,比如int类型占四个字节,char类型占一个字节等等,每个字节都在0~4,294,967,295之间都有一个对应的编号,C语言允许在程序中使用变量的地址,并可以通过地址运算符"&"得到变量的地址。
#include<stdio.h>
int main()
{
int i;
int a[10]={1,2,3,4,5,6,7,8,9,0};
char b[10]={'c','l','a','n','g','u','a','g','e'};
for(i=0;i<10;i++)
{
printf("int Address:0x%x,Value:%d\n",&a[i],a[i]);
}
printf("\n");
for(i=0;i<10;i++)
{
printf("char Address:0x%x,Value :%c\n",&b[i],b[i]);
}
return 0;
}
输出:
int
Address:0xbfb949c4,Value:1
int
Address:0xbfb949c8,Value:2
……
2、指针的定义和使用
地址就是逻辑内存上的编号(是一个常量),指针也表示一个地址(但是是地址上的一个游标,可以左右移动,在某个时刻指向某个地址,这就是指针变量)。
对指针变量定义的一般形式为:
类型说明符 *变量名;
这里的*与前面的类型说明符共同说明这是一个指针变量,类型说明符表示该指针变量所指向的变量为何种数据类型,变量名即为定义的指针变量名。
#include<stdio.h>
int main(void){
int num=1024;
int *ptr=#
printf("num address = 0x%x ,num=%d\n",&num,num);
printf("ptr=0x%x, *ptr=%d\n",ptr,*ptr);
printf("point size is: %d\n",sizeof(ptr));
return 0;
}
输出:
num address = 0x40544c4 ,num=1024
ptr=0x40544c4, *ptr=1024point size is: 8 //指针变量所占的字节
3、数组与指针的区别与联系
通过数组下标可以确定数组元素在数组中的顺序和存储地址。由于每个数组元素相当于一个变量,因此指针变量可以指向数组中的元素,也就是说可以用指针方式访问数组中的元素。
对一个指向数组元素的指针变量的定义和赋值方法,与指针标量相同:
int
a[10];
/*定义a为包含10个整型数据的数组*/
int
*p;
/*定义p为指向整型变量的指针*/
p=&a[0];
/*把a[0]元素的地址赋给指针变量p*/
数组名代表数组的首地址,也就是第0号元素的地址。
#include<stdio.h>
int main(void){
int a[10]={1,2,3};
int *p=&a[2], *p1=a;
printf("p=0x%x, *p=%d\n",p,*p);
printf("point size is: %d\n",sizeof(p));
printf("p1=0x%x, *p1=%d\n",p1,*p1);
printf("point size is: %d\n",sizeof(p1));
return 0;
}
输出:
p=0x204bd58, *p=3
point size is: 8
p1=0x204bd50, *p1=1
point size is: 8
对于指向首地址的指针p,p+i(或a+i)就是数组元素a[i]的地址,*(p+i)( 或*(a+i) )就是a[i]的值。
如果指针变量p已指向数组中的某一个元素,则p+1指向同一数组中的下一个元素。
也就是说:输出为*ptr就等同于ptr指针指向的元素。
#include<stdio.h>
int main(void){
int a[10]={1,2,3};
int *p=a;
for(int i=0;i<10;i++){
printf("P value=%d, a value=%d\n",*(p++),*(a+i));
}
return 0;
}
这样每个循环输出的都是相同的两个值。
指针可以通过++或--并修改自身值的方式移动,然而数组名本身值不可以被更改。
4、字符串与指针的用法
字符指针可以指向一个字符串,可以用字符串常量对字符指针进行初始化。
char
*str =
"www.dotcpp.com"
;
这是对字符指针进行初始化。此时,字符指针指向一个字符串的首地址。
也可以用字符数组来存放字符串。例如:
char
string[ ] =
"Welcome to dotcpp.com"
;
顺序输出字符串:
#include<stdio.h>
int main(void){
char w[]="Hello world!";
for(int i=0;w[i]!='\0';i++){
printf("%c",w[i]);
}
return 0;
}
字符串指针和字符串数组两种方式都可以访问字符串,但它们有着本质的区别:字符指针str是个变量,可以改变str使它指向不同的字符串,但不能改变str所指向的字符串常量的值。而string是一个数组,可以改变数组中保存的内容。应注意字符串指针和字符串数组的区别。
#include<stdio.h>
int main(void){
char w[]="Hello world!";
char *p="goodbye!";
w[2]='c';
for(int i=0;w[i]!='\0';i++){
printf("%c ",w[i]);
}
return 0;
}
这样可以正常输出,但是不能使用p[2]=‘c’,会报错。
5、常用字符串处理函数
C语言常用库函数大全 - C语言网
在上面九(3)中已经介绍了一部分相关的函数。具体的到后面再看。