绪论
- 机器语言
- 汇编语言
- 第三代编程语言:c语言
C语言的特点:
- 效率高
- 灵活度高
- 可移植性高
第一个程序:
# include <stdio.h>
int main(){
printf("Hello World \n");
return 0;
}
printf:格式化输出函数
转义字符
变量和常量
变量名:
- 只能是英语字母和数字或者下划线组成
- 第一个字母必须是字母或者下划线
- 区分大小写
- 不能用关键字命名
关键字:
数据类型:
常量:
定义符号常量:
标识符
字符串常量:
\0 :空字符,在c语言里表示字符串的结束位置
数据类型
基本类型
sizeof运算符:
sizeof运算符用于获得数据类型或表达式的长度
sizeof(object);//sizeof(对象);
sizeof(type_name);//sizeof(类型);
sizeof object;//sizeof对象;
#include <stdio.h>
int main() {
printf("int = %d\n",sizeof(int));//4
printf("short int = %d\n",sizeof(short));//2
printf("long int = %d\n",sizeof(long));//4
printf("long long int = %d\n",sizeof(long long));//8
printf("char = %d\n",sizeof(char));//1
printf("bool = %d\n",sizeof(_Bool));//1
printf("float = %d\n",sizeof(float));//4
printf("double = %d\n",sizeof(double));//8
printf("long double = %d\n",sizeof(long double));//12
}
signed 和 unsigned
#include <stdio.h>
int main(){
short i;
unsigned short j;
printf("%d\n",i);//-1
printf("%d\n",j);//65535
return 0;
}
取值范围
比特位
CPU能读懂的最小单位 ----- 比特位,bit,b
字节
内存机构的最小寻址单位-----字节,Byte,B
1B=8b
符号位
- 存放singned类型的存储单元中,左边第一位表示符号位。如果该位为0,表示该整数是一个正数;如果该位为1,表示该整数是一个负数
- 一个32位的整型变量,除去左边第一位符号位,剩下表示值的只有31个比特位
- 事实上计算机是用补码的形式来存放整数的值
基本数据类型的取值范围
字符和字符串
字符串:
char 变量名[数量];
变量名[索引号] = 字符;
//声明字符串
char name[5];
//给字符串赋值:
name[0] = 'F';
name[1] = 'i';
name[2] = 's';
name[3] = 'h';
name[4] = 'c';
//定义字符串
char name[5] = {'F','i','s','h','c'};
printf("\s\n",name);
printf("Hello\n"); // 会出现乱码,因为name5个空间已经填满了,没有结束标志\0的位置了
//给\0留一个位置
char name[6] = {'F','i','s','h','c','\0'};
//上述太麻烦需要计算,可以如下定义
char a[] = {'F','i','s','h','c'};
char a[] = {"Fishc"};
char a[] = "Fishc";
运算符
算术运算符
目?
双目运算符:
单目运算符: 正负号
表达式
#include <stdio.h>
#include <math.h>
int main(){
int i,j,k;
i = 1+2; //3
j = 1+2*3; //7
k = i+j+-1+pow(2,3); // 17
printf("i=%d\n",i);
printf("j=%d\n",j);
printf("k=%d\n",k);
return 0;
}
类型转换
#include <stdio.h>
int main() {
// 1+2.0 == 1.0+2.0 ?
printf("整型输出:%d\n",1+2.0); //0
printf("浮点型输出:%f\n",1.0+2.0); //3.000000
//强制转换
printf("整型输出:%d\n",1+(int)2.0); //3
printf("整型输出:%d\n",1+(int)1.8); //2
printf("整型输出:%d\n",1+(int)(1+0.8)); //2
}
分支结构
关系运算符
关系表达式
- 用关系运算符将两边的变量、数据或表达式连接起来,称之为关系表达式:
1<2 //1
a>b //1
a<=1+b //0
‘a’ +‘b’ <= ‘c’ //0
(a=3) >(b=5) //0
1 — true
0 — false
逻辑运算符
- 用逻辑运算符将两边的变量、数据或表达式连接起来,称之为逻辑表达式:
#include <stdio.h>
int main() {
int a = 3;
int b = 5;
printf("%d\n",3>1&&1<2);//1
//任何非0的数为真!
printf("%d\n",3+1||2==0);//1
printf("%d\n",!(a+b));
printf("%d\n",!0+1<1||!(3+4));//0
printf("%d\n",'a'-'b'&&'c'); //1
}
短路求值
- 短路求值又称最小化求值,是一种逻辑运算符
的求值策略。只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值。 - C语言对于逻辑与和逻辑或采用短路求值的方式
#include <stdio.h>
int main() {
int a = 3,b = 3;
(a=0) && (b=5);
printf("a=%d,b=%d\n",a,b); //a=0.b=3
(a=1) || (b=5);
printf("a=%d,b=%d\n",a,b); //a=1.b=3
return 0;
}
if语句
- 样式1
#include <stdio.h>
int main() {
int i;
printf("您老贵庚啊:");
scanf("%d",&i);
if(i>=18) {
printf("进门左拐");
}
}
- 样式2
#include <stdio.h>
int main() {
int i;
printf("您老贵庚啊:");
scanf("%d",&i);
if(i>=18) {
printf("进门左拐");
} else {
printf("慢走,不送");
}
}
- 样式3
#include <stdio.h>
int main() {
int scores;
printf("请输入你的分数:%c\n");
scanf("%d",&scores);
if(scores>=90) {
printf("A\n");
} else if(scores<90 && scores>=80) {
printf("B\n");
} else if(scores<80 && scores>=70) {
printf("C\n");
} else if(scores<70 && scores>=60) {
printf("D\n");
} else {
printf("E\n");
}
}
switch语句
#include <stdio.h>
int main() {
char ch;
printf("请输入成绩:");
scanf("%c",&ch);
switch(ch) {
case 'A':printf("你的成绩在90分以上!\n");
break;
case 'B':printf("你的成绩在80-90分之间");
break;
case 'C':printf("你的成绩在70-80分之间");
break;
case 'D':printf("你的成绩在60-70分之间");
break;
case 'E':printf("你的成绩在60分以下");
break;
default:printf("请输入有效的成绩评级");
break;
}
}
分支语句的嵌套
#include <stdio.h>
int main(){
int a,b;
printf("请输入两个数:");
scanf("%d %d",a,b);
if(a!=b){
if(a>b) {
printf("%d > %d",a,b);
} else {
printf("%d < %d",a,b);
}
} else {
printf("%d = %d",a,b);
}
}
- 悬挂else的问题
if后面都带大括号,如果没有大括号,else会跟离他近的if相搭配 - 等于号带来的问题
== 判断是否相等
= 赋值号
循环结构
while语句
#include <stdio.h>
int main() {
int i=1,sum=0;
while(i<=100) {
sum+=i;
i+=1;
}
printf("结果是:%d\n",sum);
return 0;
}
- getchar()
do…while语句
for语句
循环的基本结构
- 初始化计数器
- 循环条件
- 更新计数器
循环嵌套
应用:打印九九乘法表
#include <stdio.h>
int main() {
for (int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
printf("%d*%d=%2d ",i,j,i*j);
}
putchar('\n');
}
return 0;
}
break 和 continue
1.当它们用在循环语句的循环体时,break用于立即退出本层循环,而continue仅仅结束本次循环(本次循环体内不执continue语句后的其它语句,但下一次循环还会继续执行。
2. 如果有多层循环时,break只会跳出本层循环,不会跳出其他层的循环.
3. break可用于switch语句,表示跳出整个switch语句块,而continue则不能单独的用于switch语句。但是continue可以用于循环内部的switch语句。
4. break和continue语句在循环内的switch语句中使用时,是有区别的。在这种情况下的break是指跳出switch语句块(switch语句块的代码仍然执行)。而这种情况下的continue是指结束本次循环(不在执行switch后面的代码),进行下一次循环。
运算符
赋值运算符
赋值运算符的左边必须是一个lvalue,变量名就是lvalue
eg:int a; a = 5;
复合的赋值运算符
a = a+1; ==> a++ ;
自增自减运算符
#include <stdio.h>
int main() {
int i=5,j;
j=++i;
printf("i=%d,j=%d\n",i,j); //i=6,j=6;
i = 5;
j = i++;
printf("i=%d,j=%d\n",i,j);//i=6;j=5;
return 0;
}
逗号运算符
- 语法:表达式1,表达式2,… ,表达式n
- 逗号表达式的运算过程为从左往右逐个计算表达式
- 逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值
条件运算符
goto语句
- 语法:goto 标签;
- 开发过程中,尽量避免使用goto语句
#include <stdio.h>
int main() {
int i = 5;
while(i++) {
if(i>10) {
goto A
}
}
A: printf("Here,i=%d\n",i); // i=11
return 0;
}
注释
- // 单行注释
- /*
多行注释
*/
数组
数组的定义
- 类型 数组名[元素个数]
数组不能动态定义,但是C99标准可以动态定义数组。
#include <stdio.h>
int main() {
int n,i;
printf("请输入字符串的个数:");
scanf("%d",&n);
char a[n+1];
printf("请开始输入字符串:");
for(int i=0;i<n;i++) {
scanf("%c",&a[i]);
}
a[n] = '\0';
printf("你输入的字符串是:\s",a);
return 0;
}
//不会越界报错,输出仍为0-10;
#include <stdio.h>
int main() {
int a[10];
for(int i=0;i<=10;i++) {
a[i] = i;
}
a[10] = 111;
for(int i=0;i<=10;i++) {
printf("%d\n",a[i]);
}
return 0;
}
访问数组中的元素
- 数组名[下标]
a[0]; //访问a数组中的第一个元素
- 注意:
int a[5]; // 创建一个具有五个元素的数组
a[0]; //访问第一个元素的下标是0,不是1
a[5]; //报错,因为第五个元素的下标是a[4]
循环跟数组的关系
- 这是因为我们常常需要使用循环来访问数组
#define <stdio.h>
#define NUM 10
int main() {
int s[NUM];
int i,sum=0;
for(i=0;i<10;i++) {
printf("请输入第%i位同学的成绩",i+1);
scanf("%d",&s[i]);
sum+=s[i];
}
printf("成绩录入完毕,该次考试的平均分是:%.2f\n",(double)sum/num);
return 0;
}
数组的初始化
- 将数组中所有元素初始化为0,可以这么写:
int a[10] = {0};//事实上这里只是将第一个元素赋值为0
int a[10] = {1};//事实上这里只是将第一个元素赋值为1,其余9个数都为0
如果是赋予不同的值,那么用逗号分隔开即可:
int a[10] = {1,2,3,4,5,6,7,8,9,0};
还可以只给一部分元素赋值,未被赋值的元素自动初始化为0:
int a[10] = {1,2,3,4,5,6};
有时候还可以偷懒,可以只给出各个元素的值,而不指定数组的长度(因为编译器会根据值得个数自动判断数组得长度)
int a[] = {1,2,3,4,5,6,7,8,9,0};
C99增加了一种新特性:指定初始化得元素。这样就可以只对数组中得某些指定元素进行初始化赋值,而未被赋值得元素自动初始化为0;
int a[10] = {[3]=3,[5]=5,[8]=8};
字符数组
- 字符串常量
- 字符数组
字符串处理函数
获取字符串的长度 : strlen
sizeof
包含最后的“\0”结束字符strlen
不包含最后的“\0”结束字符,计算的是字符串长度
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "I love FishC.com!";
printf("sizeof str = %d\n",sizeof(str));//18
printf("strlen str = %u\n",strlen(str));//17
return 0;
}
拷贝字符串:strcpy和strncpy
#include <stdio.h>
#include <string.h>
int main(){
char str1[] = "Original String";
char str2[] = "New String";
char str3[100];
strcpy(str1,str2);
strcpy(str3,"Copy Successful!");
printf("str1:%s\n",str1);
printf("str2:%s\n",str2);
printf("str3:%s\n",str3);
return 0;
}
- strncpy 的使用
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "To be or not to be";
char str2[40];
strncpy(str2,str1,5);
str2[5] = "\0";
printf("str2:%s\n",str2);// To be;
return 0;
}
连接字符串:strcat 和 strncat
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "I love";
char str2[] = "FishC.com";
strcat(str1," ");
strcat(str1,str2);
printf("str1:%s\n",str1);
return 0;
}
- strncpy 和 strncat的区别
strncat在连接后总是会自动追加一个结束符
比较字符串:strcmp 和 strncmp
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "FishC.com!";
char str2[] = "FishC.com!";
if(!strcmp(str1,str2)) {
printf("两个字符串完全一致!\n");
} else {
printf("两个字符串存在差异!\n");
}
return 0;
}
strncmp: 对比两个字符串前n个字符
二维数组
定义
二维数组的访问
二维数组的初始化
- 由于二维数组在内存中是线性存放的,因此可以将所有的数据写在一个花括号内:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
- 为了更直观地表示元素的分布,可以用大括号将每一行的元素括起来:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
- 二维数组也可以仅对部分元素赋初值
int a[3][4] = {{1},{5},{9}};
- 如果希望整个二维数组初始化为0,那么直接在大括号里写一个0即可
int a[3][4] = {0};
- C99同样增加了一种新特性:指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为0;
int a[3][4] = {[0][0] = 1,[1][1] = 2,[2][2] = 3};
- 二维数组的初始化也能偷懒,让编译器根据元素的数量计算数组的长度。但只有第1维的元素个数可以不写,其他维度必须写上:
int [][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
#include <stdio.h>
int main() {
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i=0;i<3;i++) {
for(int j=0;j<4;j++) {
printf("%d",a[i][j]);
}
printf("\n");
}
}
数组的转置:
#include <stdio.h>
int main() {
int a[4][5] = {
{80,92,85,86,99},
{78,65,89,70,99},
{67,78,76,89,99},
{88,68,98,90,99}
};
int i,j;
for(i = 0;i<4;i++) {
for(j = 0;j<3;j++) {
printf("%d ",a[j][i]);
}
printf("\n");
}
return 0;
}
指针
指针和指针变量
- 定义指针变量
类型名 *指针变量名
char *pa; // 定义一个指向字符型的指针变量
int *pb; //定义一个指向整型的指针变量
取地址运算符和取值运算符
- 如果需要获取某个变量的地址,可以使用取地址运算符(&):
char *pa = &a; int *pb = &f;
- 如果需要访问指针变量指向的数据,可以使用取值运算符(*):
printf("%c,%d\n",*pa,*pb);
#include <stdio.h>
int main() {
char a = 'F';
int f = 123;
char *pa = &a;
int *pb = &f;
printf("a = %c\n",*pa);
printf("f = %d\n",*pb);
*pa = 'C';
*pb += 1;
printf("now,a=%c\n",*pa);
printf("now,f=%d\n",*pb);
printf("sizeof pa = %d\n",sizeof(pa));//8
printf("sizeof pb = %d\n",sizeof(pb));//8
printf("the addr of a is:%p\n",pa);//000000000065FE0F
printf("the addr of b is:%p\n",pb);//000000000065FE08
return 0;
}
在不同的系统中,C语言指针的大小是不同的。
在32位系统(即x86)中,指针的大小为4字节。
在64位系统(即x64)中,指针的大小为8字节。
指针和数组
#include <stdio.h>
int main() {
int a;
int *p = &a;
printf("请输入一个整数");
scanf("%d",&a);
printf("a=%d\n",a);
printf("请重新输入一个整数");
scanf("%d",p);
printf("a = %d\n",a);
return 0;
}
- 数组名的真实身份
数组名其实是数组第一个元素的地址!
#include <stdio.h>
int main() {
char str[128];
printf("请输入鱼C的域名:");
scanf("%s",str);
printf("鱼C工作室的域名是:%s\n",str);
printf("str的地址是:%p\n",str);
printf("str的地址是:%p\n",&str[0]);
return 0;
}
指向数组的指针
指针的运算
当指针指向数组元素的时候,我们可以对指针变量进行加减运算,这样做的意义相当于指向距离指针所在位置向前或向后第n个元素
对比标准的下标法访问数组元素,这种使用指针进行间接访问的方法叫做指针法
需要郑重强调的是:p+1并不是简单地将地址加1,而是指向数组的下一个元素。
指针数组和数组指针
- 指针和数组的区别
//计算一个字符串的字符个数,不用现成函数eg:strlen
#include <stdio.h>
int main() {
char str[] = "I love FishC.com";
int count = 0;
while(*str++ != '\0')
{
count++;
}
printf("总共有%d个字符!\n",count);
return 0;
}
报错:
lvalue:
#include <stdio.h>
int main() {
char str[] = "I love FishC.com";
char *target = str;
int count = 0;
while(*target++ != '\0')
{
count++;
}
printf("总共有%d个字符!\n",count);//16
return 0;
}
结论:数组名只是一个地址,而指针是一个左值
什么是左值?
说的通俗一点就是可以放在赋值号左边的叫左值 就是可以被赋值的。是一个地址。左值一般是是一个变量可以被赋值的变量。(const变量是一个例外只能做右值)
什么是右值(r-value):
同样我们可以说是可以放在赋值号右边的,就是可以给其他变量赋值的,它可以是一个变量也可以是一个常量也可以是一个表达式。是一个内容
- 指针数组和数组指针
int *p1[5];//指针数组
int (*p2)[5];//数组指针
- 指针数组
结论:指针数组是一个数组,每个数组元素存放一个指针变量
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *p1[5] = {&a,&b,&c,&d,&e};
int i;
for(i=0;i<5;i++) {
printf("%d\n",*p1[i]);
}
}
#include <stdio.h>
int main() {
char *p1[5] = {
"1233",
"qwe",
"12s",
"asxd",
"qwe"
};
int i;
for(i=0;i<5;i++) {
printf("%s\n",p1[i]);
}
}
- 数组指针
结论:数组指针是一个指针,它指向的是一个数组
#include <stdio.h>
int main() {
int (*p2)[5] = {1,2,3,4,5};
int i;
for(i=0;i<5;i++) {
printf("%d\n",*(p2+i));
}
return 0;
}
报错:
修改以后:
#include <stdio.h>
int main() {
int temp[5] = {1,2,3,4,5};
int (*p2)[5] = temp;
int i;
for(i=0;i<5;i++) {
printf("%d\n",*(p2+i));
}
return 0;
}
报错:
这样是没问题的:
#include <stdio.h>
int main() {
int temp[5] = {1,2,3,4,5};
int *p2 = temp;
int i;
for(i=0;i<5;i++) {
printf("%d\n",*(p2+i));
}
return 0;
}
再次修改:
#include <stdio.h>
int main() {
int temp[5] = {1,2,3,4,5};
int (*p2)[5] = &temp;
int i;
for(i=0;i<5;i++) {
printf("%d\n",*(*p2+i));
}
return 0;
}
指针和二维数组
- 二维数组的定义
#include <stdio.h>
int main() {
int array[4][5] = {0};
printf("sizeof int:%d",sizeof(int));
printf("array:%p\n",array);
printf("array+1:%p\n",array+1);
}
结果:
sizeof int:4
array:000000000065FDD0
array+1:000000000065FDE4
*(array+1)表示的是什么?
#include <stdio.h>
int main() {
int array[4][5] = {0};
int i,j,k=0;
for(i=0;i<4;i++) {
for(j=0;j<5;j++) {
array[i][j] = k++;
}
}
printf("*(array+1):%p\n",*(array+1));
printf("array[1]:%p\n",array[1]);
printf("&array[1][0]:%p\n",&array[1][0]);
printf("**(array+1):%d\n",**(array+1));
return 0;
}
结果:
*(array+1):000000000065FDD4
array[1]:000000000065FDD4
&array[1][0]:000000000065FDD4
**(array+1):5
((array+1)+3)表示的是什么?
#include <stdio.h>
int main() {
int array[4][5] = {0};
int i,j,k=0;
for(i=0;i<4;i++) {
for(j=0;j<5;j++) {
array[i][j] = k++;
}
}
printf("*(array+1):%p\n",*(array+1));
printf("array[1]:%p\n",array[1]);
printf("&array[1][0]:%p\n",&array[1][0]);
printf("**(array+1):%d\n",**(array+1));
printf("*(*(array+1)+3):%d\n",*(*(array+1)+3));//8
printf("array[1][3]:%d\n",array[1][3]);//8
return 0;
}
结论:
数组指针和二维数组
#include <stdio.h>
int main() {
int array[2][3] = {{0,1,2},{3,4,5}};
int (*p)[3] = array;
printf("**(p+1):%d\n",**(p+1));
printf("**(array+1):%d\n",**(array+1));
printf("array[1][0]:%d\n",array[1][0]);
printf("*(*(p+1)+2):%d\n",*(*(p+1)+2));
printf("*(*(array+1)+2):%d\n",*(*(array+1)+2));
printf("array[1][2]:%d\n",array[1][2]);
return 0;
}
void指针和NULL指针
#define <stdio.h>
int main() {
void a;
return 0;
}
报错:
- void指针
我们把它称之为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。
#include <stdio.h>
int main() {
int num = 1024;
int *pi = #
char *ps = "FishC";
void *pv;
pv = pi;
printf("pi:%p,pv:%p\n",pi,pv);
pv = ps;
printf("ps:%p,pv:%p\n",ps,pv);
}
结果:
pi:000000000065FE04,pv:000000000065FE04
ps:0000000000404000,pv:0000000000404000
#include <stdio.h>
int main() {
int num = 1024;
int *pi = #
char *ps = "FishC";
void *pv;
pv = pi;
printf("pi:%p,pv:%p\n",pi,pv);
printf("*pv:%s\n",*(int *)pv);
pv = ps;
printf("ps:%p,pv:%p\n",ps,pv);
printf("*pv:%s\n",(char *)pv);
}
- NULL指针
#include <stdio.h>
int main() {
int *p1;
int *p2 = null;
printf("%d\n",*p1);//自动生成一个值
printf("%d\n",*p2);//报错!
}
当你还不清楚要将指针初始化为什么地址时,请将它初始化为NULL;在对指针进行解引用时,先检查该指针是否为NULL。这种策略可以为今后编写大型程序节省大量得调试时间。
- NULL不是NUL
NULLL用于指针和对象,表示控制,指向一个不被使用的地址;而’\0’表示字符串得结尾。
指向指针的指针
#include <stdio.h>
int main() {
char *cBooks[] = {
"C程序设计语言",
"C专家编程",
"C和指针",
"C陷阱与缺陷",
"C Primer Plus",
"带你学C带你飞"};
char **byFishC;
char **jialoves[4];
int i;
byFishC = &cBooks[5];
jialoves[0] = &cBooks[0];
jialoves[1] = &cBooks[1];
jialoves[2] = &cBooks[2];
jialoves[3] = &cBooks[3];
printf("FishC出版的图书有:%s\n",*byFishC);
for(i=0;i<4;i++){
printf("%s\n",jialoves[i]);
}
return 0;
}
- 指针数组和指向指针的指针
至少有两个好处:
- 避免重复分配内存
- 只需要进行一处修改
- 数组指针和二维数组
#include <stdio.h>
int main() {
int array[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = array;
int i;
for(i=0;i<10;i++) {
printf("%d\n",*(p+i));
}
return 0;
}
二维数组:
#include <stdio.h>
int main() {
int array[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}};
int *p = array;
int i,j;
for(i=0;i<3;i++) {
for(j=0;j<4;j++) {
printf("%2d\n",*(*(p+i)+j));
}
printf("\n");
}
return 0;
}
报错:
改:
#include <stdio.h>
int main() {
int array[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}};
//int *p = array;
int i,j;
for(i=0;i<3;i++) {
for(j=0;j<4;j++) {
printf("%2d ",*(*(array+i)+j));
}
printf("\n");
}
return 0;
}
原因:c语言没有那么的智能,他没有理解array是一个二维数组,他只吧array当成了一个地址
常量和指针
常量的几种表示方法
- 520,‘a’,3.14
- 或者这样:
#define PRICE 520
#define A 'a'
#define PI 3.14
- 还可以使用const关键字修饰:
const int price = 520;
const char a = 'a';
const float pi = 3.14;
指向常量的指针
- 指针可以修改为指向不同的常量
- 指针可以修改为指向不同的变量
- 可以通过解引用来读取指针指向的数据
- 不可以通过解引用修改指针指向的数据
#include <stdio.h>
int main() {
int num = 520;
const int cnum = 880;
const int *pc = &cnum;
printf("cnum:%d,&cnum:%p\n",cnum,&cnum);
printf("*pc:%d,pc:%p\n",*pc,pc);
pc = #
printf("num:%d,&num:%p\n",num,&num);
printf("*pc:%d,pc:%p\n",*pc,pc);
num = 1024;
printf("*pc:%d,pc:%p\n",*pc,pc);
return 0;
}
运行结果:
cnum:880,&cnum:000000000065FE10
*pc:880,pc:000000000065FE10
num:520,&num:000000000065FE14
*pc:520,pc:000000000065FE14
*pc:1024,pc:000000000065FE14
常量指针
- 指向非常量的常量指针
指针自身不可以被修改
指针指向的值可以被修改
- 指向常量的常量指针
指针自身不可以被修改
指针指向的值也不可以被修改
- 指向“指向常量的常量指针”的指针
函数
例子:
#include <stdio.h>
void print_C();
void print_C() {
printf(" ##### \n");
printf("## ##\n");
printf("## \n");
printf("## \n");
printf("## ##\n");
printf(" ##### \n");
}
int main() {
print_C();
return 0;
}
函数的定义
函数的声明
函数的参数和返回值
// 计算1-n的和
#include <stdio.h>
int sum(int n);
int sum(int n) {
int sum = 0;
for (int i=0;i<=n;i++){
sum += i;
}
return sum;
}
int main() {
int n;
printf("请输入n的值:");
scanf("%d",&n);
printf("1+2+3+4...+(n-1)+n的结果是:%d",sum(n));
return 0;
}
// 编写一个函数max,接收两个整型参数,并返回它们中的较大值
#include <stdio.h>
int big(int a,int b);
int big(int a,int b){
int max = a > b?a : b;
return max;
}
int main(){
int a,b;
printf("请输入两个整型参数:");
scanf("%d,%d",&a,&b);
printf("两个数中较大的值是:%d",big(a,b));
return 0;
}
参数和指针
形参和实参
传值和传址
//将两数互换
#include <stdio.h>
int swap(int x,int y);
int swap(int x,int y){
int temp = x;
x = y;
y = temp;
printf("%d %d",x,y);
}
int main() {
int x = 3;
int y = 4;
swap(x,y);
return 0;
}
传数组
结果:
可变参数
#include <stdio.h>
#include <stdarg.h>
/*
va_list
va_start
va_arg
va_end
*/
// va----variable-argument
int sum(int n,...);
int sum(int n,...) {
int i,sum = 0;
va_list vap;
va_start(vap,n);
for(i = 0;i < n;i++) {
sum+=va_arg(vap,int);
}
va_end(vap);
return sum;
}
int main() {
int result;
result = sum(3,1,2,3);
printf("result = %d\n",result);
result = sum(5,1,2,3,4,5);
printf("result = %d\n",result);
result = sum(6,1,2,3,4,5,6);
printf("result = %d\n",result);
return 0;
}
指针函数
#include <stdio.h>
char *getWord(char);
char *getWord(char c) {
switch(c)
{
case 'A': return "Apple";
case 'B': return "Banana";
case 'C': return "Cat";
case 'D': return "Dog";
default:return "None";
}
}
int main() {
char input;
printf("请输入一个字母: ");
scanf("%c",&input);
printf("%s\n",getWord(input));
}
使用指针变量作为函数的返回值,就是指针函数。
不要返回局部变量的指针!