C语言--小甲鱼

绪论

  1. 机器语言
  2. 汇编语言
  3. 第三代编程语言:c语言

C语言的特点:

  1. 效率高
  2. 灵活度高
  3. 可移植性高

第一个程序:

# 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 = &num;
 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 = &num;
 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;
}
  • 指针数组和指向指针的指针
    至少有两个好处:
  1. 避免重复分配内存
  2. 只需要进行一处修改
  • 数组指针和二维数组
#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 = &num;
	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));
	

}

使用指针变量作为函数的返回值,就是指针函数。

不要返回局部变量的指针!

函数指针

在这里插入图片描述


版权声明:本文为qq_45067482原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。