C语言初识

 

初识C语言

dir 查看文件的目录

C程序的基本框架

#include<stdio.h>
int main()
{
    //执行的代码
	return 0;
}

gcc demo.c -o demo //把a,exe 改成demo

echo %errorlevel% 错误等级

数据表现形式

变量

四要素 变量值 类型 变量名 存储单元

int 4个字节,float 4   int float 编码格式不一样不能直接用不同的占位符直接输出
float 
double
char
char*
 

驼峰命名法

int OneYear = 20;   
void One_Year ()    
{
    //功能模块 函数写的东西
}

printf() 输出有 原样输出 %占位符/格式字符printf("%d",a)

printf("小黑子你好"); //原样输出
printf("%d",a);  //%占位符输出

printf() 小练习 加减乘除

输入 scanf()

scanf()//z最好 多个变量 多个scanf()

原样输入,要输入相应的内容

scanf("%d",&data); // 不要在scanf里面+空格 和其他的符号 &取地址符号
scanf("%d%d%%d"&a,&b,&c); //正确方式
scanf("a=%d,b=%d,c=%d",&a,&b,&c);//这个要将括号里面的输入完全(错误方式)
1:scanf()函数混合输入

以上方法容易出错

其他输入输出方式
getchar();
putchar();
puts();
gets();

 

练习题
练习题1

输入大写字母 输出小写字母

练习题2

输入两个数 获得两个数的加减乘除 的值

练习题3

输入字符串 将输入的字符串加密 (在原来的基础之上往后移动四位)

总结

#include<stdio.h>
int main()
{
    //原样输出
    printf("jjjjj");
    // %占位符输出
    int a;
    printf("%d",a); // %d %c %f  %n.mf  %-n.mf  %p %x 
    //输入  scanf();
    int b;
    scanf("%d",b); //scanf()最好 一个i一个i用,
    //原样输入
    printf("%d",b);
    //其他输入\输出方法
    putchar(); 输出一个字符
        puts(); 输出字符串
    getchar();  获得字符(你要输入的字符串)
        gets();  获取字符串
    
    
    
    return 0;
}
#include<stdio.h>
int main()
{
    int data;
    char c = 'c';
    char* f = "jdio";
    float ff ;
    
    printf("原样输出\n");
    printf("占位符输出%d",data);
    scanf("%d",&data);
    putchar('');
    getchar();
    puts();
    gets();
    /*puts(); 与printf();的区别
    puts(); 只能输出字符串,自动换行
    printf(); 支持多样输出。不自动换行;
    
    */
}

换大小写字母

#include<stdio.h>
int main()
{
    char a ;
    char b;
    printf("请输入字母\n");
    scanf("%c",&a);
    b=a+32;
    printf("%c",a+32);
    return 0;
}

2流程控制

1,控制语句

#include<stdio.h>
int main()
{
    if(){
        else if()
        {
            
        }
    }else{
        
    }
    for(;;)
    {
        
    }
    switch()
    	case:
    
    
    return 0;
}

2 交换两个数字

3级交换数字

交换数字的时候要注意,要三次交换,先a<b a<c b<c

3逻辑运算

#或 与 非

逻辑运算if() 小练习 逻辑判断或与非

if() else() 小练习

if 嵌套语句运用

#include<stdio.h>
/*
嵌套用法 
*/
int main()
{
	int data1;
	int data2;
	int data3;
	printf("相亲角\n");
	puts("身体好么 好1,不好0");
	scanf("%d",&data1);
	if(data1 ==1)
	{
		printf("可以考虑交友\n");
		puts("性格好么 好1 不好0");
		scanf("%d",&data2);
		if(data2 == 1)
		{
			printf("身体又好又温柔,真好\n");
			puts("你有钱么");
			scanf("%d",&data2);
			if(data2 == 1)
			{
				printf("真名天子\n");
			}else{
				puts("身体很好,性格倒是不错,不过实力便是弱了些,再见");
			}
			
		}else{
			puts("性格不合适");
		}
		
	}else{
		printf("再见\n");
	}
	puts("相亲结束");
	
	return 0;
}

switch case 用法

错误
a3.6_switch_case.c: In function 'main':
a3.6_switch_case.c:28:2: error: too few arguments to function 'gets'
  gets();
  ^~~~
In file included from a3.6_switch_case.c:1:
C:/MinGW64/x86_64-w64-mingw32/include/stdio.h:658:17: note: declared here
   char *__cdecl gets(char *_Buffer) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;
   
解决方法

根据提供的错误信息,可以看出在你的代码文件 “a3.6_switch_case.c” 的第28行中,调用了函数 ‘gets’,但是没有传入任何参数。然而,根据C语言的标准库函数,‘gets’ 函数需要至少一个参数来接收输入的字符串。

需要修复这个问题,你可以在调用 ‘gets’ 函数时传入一个字符数组作为参数,用于接收输入的字符串。例如:

char input[100];
gets(input);
此外,需要注意的是,‘gets’ 函数在很多编译器中已经被标记为不安全和不推荐使用的函数,因为它容易导致缓冲区溢出的安全漏洞。建议使用更安全的函数替代,如 ‘fgets’ 函数。使用 ‘fgets’ 函数的示例代码如下:

char input[100];
fgets(input, sizeof(input), stdin);
使用 ‘fgets’ 函数时,需要指定接收输入的字符数组的大小,并传入 ‘stdin’ 作为最后一个参数,表示从标准输入中读取输入。

希望这些信息对你有所帮助!如果有任何其他问题,请随时提问。

正确代码

#include<stdio.h>
int main()
{
	int data;
	char cdata;
	puts("please input the number");
	scanf("%d",&data);
	switch(data)
	{
		case 1:
		puts("1");
		break;//执行之后,跳出switch
		case 2:
		puts("2");
		break;
		case 3:
		puts("3");
		break;
		case 4:
		puts("4");
		break;
		default :
		puts("other");
		break;
	}
	getchar();
	puts("please input string");
	getchar(); // 吸收回车按键
	//gets(); //gets 不建议使用 可以用 fgets();
	scanf("%c",&cdata);
	switch(cdata){
		case 'a':
		puts("ikun");
		break;
		case 'b':
		puts("小黑");
		break;
		default :
		puts("炒粉");
		break;
	}
	
	return 0;
}

switch 学生成绩
#include<stdio.h>
int main()
{
	int score;
	puts("请输入成绩");
	scanf("%d",&score);
	switch(score/10){
		case 0:;
		puts("比蔡徐坤还蔡徐坤");
		break;
		case 1:
		puts("你是真正的蔡徐坤");
		break;
		case 2:
		puts("进步空间巨大");
		break;
		case 3:
		puts("进步空间大");
		break;
		case 4:
		
		case 5:
		puts("59半步斗之气");
		break;
		case 6:
		puts("60及格");
		break;
		case 7:
		puts("70中等");
		break;
		case 8:
		puts("80良好");
		break;
		case 9:
		puts("90优秀");
		break;
		case 10:
		puts("100顶尖");
		break;
		default:
		puts("异常");
		break;
		
	}
	return 0;
}

调试结果

计算折扣两种方法

if
#include<stdio.h>
int main()
{
	float money;
	int luCheng;
	puts("您的路程为");
	scanf("%d",&luCheng);
	if(luCheng>=0 && luCheng<=100)
	{
		money =2;
		printf("您行驶距离为%d\n您骑行的费用为%4.2f",luCheng,money);
	}else if(luCheng>100 && luCheng<=500)
	{
		money = 2+(float)luCheng*0.008;
		printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
	}
	else if(luCheng>500 && luCheng<=1000)
	{
		money = 2+(float)luCheng*0.006;
		printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
	}
	else 
	{
		money = 2+(float)luCheng*0.004;
		printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
	}
	
	return 0;
}
switch case
#include<stdio.h>
int main()
{
	float money;
	int luCheng;
	puts("please input lucheng number");
	scanf("%d",&luCheng);
	switch(luCheng)
	{
		case 0 ~ 100:
			money = 2.00;
			printf("您行驶的距离为%d\n您骑行的费用为%4.4f",luCheng,money);
			break;
		case 101~ 500:
			money = 2.00+luCheng*0.008;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 501~ 1000:
			money = 2.00+luCheng*0.006;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 999~ 5000:
			money = 2.0+luCheng*0.005;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 5001~10000	:
			money = 2.0+luCheng*0.004;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		default:
			money = 2.0+luCheng*0.3;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			
			break;
	}
	return 0;
}
#include<stdio.h>
int main()
{
	float money;
	int luCheng;
	puts("please input lucheng number");
	scanf("%d",&luCheng);
	switch(luCheng)
	{
		case 100:
			money = 2.00;
			printf("您行驶的距离为%d\n您骑行的费用为%4.4f",luCheng,money);
			break;
		case 500:
			money = 2.00+luCheng*0.008;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 1000:
			money = 2.00+luCheng*0.006;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 5000:
			money = 2.0+luCheng*0.005;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		case 10000:
			money = 2.0+luCheng*0.004;
			printf("您行驶的距离为%d\n您骑行的费用为%4.2f",luCheng,money);
			break;
		default:
			//money = 2.0+luCheng*0.3;
			printf("您行驶的距离为");/*%d\n您骑行的费用为%4.2f",luCheng,money)*/
			
			break;
	}
	return 0;
}

作业

计算分段函数
#include<stdio.h>



int main()
{
	int y;
	int x;
	puts("请输入一个数");
	scanf("%d",&x);
	if(x<1)
	{
		y=x;
		printf("y的值为%d",y);
	}else if(x>=1&&x<10)
	{
		y=2*x-1;
		printf("y的值为%d=2*%d-1",y,x);
	}else if(x>=10)
	{
		y=3*x-11;
		printf("y的值为%d=3*%d-11\n",y,x);
	}
	
	return 0;
}

运行结果

个人成绩等级划分
#include<stdio.h>
int main()
{
	int score;
	puts("please input score");
	scanf("%d",&score);
	switch(score/10)
	{
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
			printf("E不鸡格\n");
			break;
		case 6:
			printf("D鸡哥\n");
			break;
		case 7:
			printf("C中等\n");
			break;
		case 8:
			printf("B良好\n");
			break;
		case 9:
			printf("A优秀\n");
			break;
		case 10:
			printf("S顶级\n");
			break;
		default:
			printf("异常\n");
		break;
		
	}
	
	return 0;
}

4 循环运算 for while do while

while

#include<stdio.h>
void jj()
{
	int time;
	int a;
	while(a){
		
		a=time--;
		puts("徐坤");
	}
}
int main()
{
	int time;
	unsigned int a;
	time=2;
	
	
	while(a)
	{
		a=time--;
		puts("小黑");
	}
	return 0;
}

t--和--t的区别

t--(后置自减)先用 t 后减

 

--t (前置自减)t 先减 后用

int t = 5;
int result1 = t--;  // result1 的值为 5,然后 t 的值变为 4
int t = 5;
int result2 = --t; // result2 的值先减为 4 ,然后 t 为4

for

九九乘法表
#include<stdio.h>
//乘法表
int main()
{
	int a;
	int b;
	int c;
	for(a=1;a<10;a++)
	{
		for(b=1;b<a;b++)
		{
			c=a*b;
			printf("%d  =  %d*%d\n",c,a,b);
		}
        printf("\n");
		
	}
	printf("结束\n");
	return 0;
}

break continue 的区别
for(renshu = 1; renshu <=1000; renshu++)
	{
		puts("请输入捐款数目:");
		scanf("%d",&data);
		
		ZongMoney = data+ZongMoney;
		if(ZongMoney >= 100000)
		{
			puts("款项已经达到标准");
			printf("钱总共有%d",ZongMoney);
			break; //结束整个循环
		}
	}
for(data =100;data<=200;data++)
	{
		if(data%3 ==0)//被3整除的数
		{
			continue;  //中断一个循环
		}
		printf("%d\n",data);
	}
求取最小公倍数
#include<stdio.h>
//取最小公倍数 先比较两个数的大小,最小公倍数一定>=最大的数,
//然后依次取模
int main()
{
	int data1;
	int data2;
	data1 =0;
	data2 = 0;
	int m;
	puts("请输入两个数");
	scanf("%d%d",&data1,&data2);
	m = data1 < data2?data1:data2;
	
	for(m;;m++)
	{
		if(m%data1==0 && m%data2==0)//
		{
			break;//中断整个循环
		}
	}
	printf("%d",m);
	
	return 0;
}
求取最大公约数
#include<stdio.h>
int main()
{
	int data1;
	int data2;
	puts("please input tow number ");
	scanf("%d%d",&data1,&data2);
	int i;
	int m;
	int n;
	for(i=1;i<=data1 && i<=data2;i++)
	{
		if(data1%i ==0&& data2%i ==0)
		{
			m=i;
			//break;
		}
	}
	n = data1*data2/m; //最小公倍数 == data1*data2 /最大公约数
	
	printf("最大公约数是%d\n最小公倍数是%d",m,n);
	return 0;
}
水仙花数
#include<stdio.h>
//求取 水仙花数  ==一个3位数 各位数字立方之和 
// 153 ==1^3+5^3+3^3
int main()
{
	int i;
	int a;
	int b;
	int c;
	for(i=100;i<=999;i++)
	{
		c = i%10;
		b = (i/10)%10;
		a = i/100;
		if(i == a*a*a+b*b*b+c*c*c){
		printf(" %d ",i);
		}
	}
	
	return 0;
}

5,总结

#1 if语句

基本用法

if()//判断条件
{
    
    //执行的模块
}else
{
    
}

连续判断

if()
{
    
}else if()
{
    
}else if()
{
    
}

嵌套用法

if()
{
    if()
    {
        if()
        {
            
        }else{
            
        }
    }else{
        
    }
    
}else{
    
}

switch case 用法

switch ()
{
    case 1:
        //执行的模块
        break;
    case 2:
        break;
    case 3:
        break;
    default :
        break;
}

switch case 一般用于需要多种判断条件的时候

循环三剑客

fou
//基本用法
for(int i = 0;i<100;i++)
{
    puts("jjjj");
}
//嵌套用法
int k;
for(int i;i<9;i++)
{
    for(int j;j<i;j++)
    {
        k = i*j;
        printf("%d=%d*%d",k,j,i);
        
    }
    printf("\n");
}
九九乘法表
#include<stdio.h>
//乘法表
int main()
{
	int a;
	int b;
	int c;
	for(a=1;a<10;a++)
	{
		for(b=1;b<=a;b++)
		{
			c=a*b;
			printf("  %d=%d*%d  ",c,a,b);
		}
		printf("\n");
		
	}
	printf("结束\n"); 
	return 0;
}

while
//while 基本用法
while(time <100) //范围
{
    puts("积极");//循环体
    time--;  //循环体
}

do while
do{
    
}while()
//日后再说,先循环,后判断条件

作业

求取最大公约数
#include<stdio.h>
int main()
{
	int data1;
	int data2;
	puts("please input tow number ");
	scanf("%d%d",&data1,&data2);
	int i;
	int m;
	int n;
	for(i=1;i<=data1 && i<=data2;i++)
	{
		if(data1%i ==0&& data2%i ==0)
		{
			m=i;
			//break;
		}
	}
	n = data1*data2/m; //最小公倍数 == data1*data2 /最大公约数
	
	printf("最大公约数是%d\n最小公倍数是%d",m,n);
	return 0;
}
最小公倍数
水仙花数
三位数 = 个个位数 三次方之和

3数组

1,定义一个数组

int a[10];  
// 类型符  数组名  常量表达式

2,初始化数组

    int a[10]={1,2,3,4,5,6,7,8,9,10};//全部赋值
	int b[10]={1,2,3,4};//部分赋值
	int c[10]={0}; //全部为零
   	int d[]={1,3,3,4,4,};//会根据大括号判断有多少个数

sizeof()

	sizeof()//关键字 计算括号中对应数据的内存空间的大小
	size_a = sizeof(a)/sizeof(a[0]);//计算个数
	size_c = sizeof(c)/sizeof(c[0]);
	size_b = sizeof(b)/sizeof(b[0]);

3,数组编程案列

数组初始化级逆序输出

#include<stdio.h>
int main()
{
	int a[10];
	int data;
	int size;
	int sizea;
	int sizeb;
	sizea = sizeof(a);
	sizeb = sizeof(a[0]);
	size = sizeof(a)/sizeof(a[0]);  
	for(data = 0;data<size;data++){
		a[data] = data;
	}
	puts("初始化");
	for(data = 0;data<size;data++)
	{
		printf("地址%p  数据%d\n",&a[data],a[data]);
	}
	puts("逆序输出");
	for(data = size;data>=0;data--)
	{
		a[data] = data;
	}
	for(data = size ;data>=0;data--)
	{
		printf("%p  %d\n",&a[data],a[data]);
	}
	printf("dome\n");
	printf("%d\n",sizea);
	printf("%d",sizeb);
	
	return 0;
}

斐波那契数列输出

#include<stdio.h>
// a[i] = a[i-1]+a[i-2]
int main()
{
	unsigned int arr[10];
	int i;
	int size;
	arr[0]=0;
	arr[1]=1;
	size = sizeof(arr)/sizeof(arr[0]);
	for(i=2;i<size;i++)
	{
		
		arr[i]=arr[i-1]+arr[i-2];
	}
	for(i=0;i<size;i++)
	{
		printf(" %d  ",arr[i]);
	}
	printf("dome");
	
	return 0;
}

冒泡排序法

#include<stdio.h>
int main()
{
	
	int arr[]={12,34,5,5,5,666,53,343,4343,4332,53,432,534,2,5432};
	int i;
	int j;
	int len;
	int temp;
	
	len = sizeof(arr)/sizeof(arr[0]);
	for(i=0;i<len-1;i++)
	{
		for(j=0;j<len-i-1;j++)
		{
			if(arr[j]<arr[j+1])
			{
				temp = arr[i];
				arr[i] =arr[i+1];
				arr[i+1] = temp;
				
			}
			
		}
		
	}
	for(i=0;i<len;i++)
	{
		printf("%d ",arr[i]);
	}
	
	return 0;
}
0123
01 2 3 42 1 3 42 3 1 42 3 4 1 1
12 3 43 2 43 4 22
23 44 333
244

$$
i = len -1; j = len-1-i;
$$

简单选择排序法

#include<stdio.h>
int main()
{
	int i;
	int j;
	int temp;
	int arr[]={1,2,5,6,66,5,55,6,22,6,22,55};
	int len;
	len = sizeof(arr)/sizeof(arr[0]);
	for(i=0;i<len-1;i++)
	{
		for(j=i+1;j<len;j++)
		{
			if(arr[i]<arr[j])
			{
				temp = arr[i];
				arr[i] = arr[j];
				arr[j] = temp;
			}
			
		}
		
	}
	for(i=0;i<len;i++)
	{
		printf(" %d ",arr[i]);
		
	}
	printf("\ndome");
	return 0;
}

$$
i < len -1 j<len i表示比对的轮数 j 就是i后一位数
$$

4,二维数组

#include<stdio.h>
int main()
{
	//定义一个二维数组
	int arr[2][3]={{11,33,44},{33,55,66}};
	int arr1[3][3]={1,2,3,4,5,6,7,8,9};
	int i,j;
	//遍历二维数组
	for(i=0;i<2;i++)
	{
		for(j=0;j<3;j++)
		{
			printf("%d  ",arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

二维数组初始化

// 二维数组 行可以省掉,但是列不能省
#include<stdio.h>
//3x4数组,求其最大的元素并输出,行和列
int main()
{
	int i;
	int j;
	int max;
	
	int arr[3][4]={1,3,22,34,54,54,32,3,7,66,5,234};
	max = arr[0][0];
	
	for(i=0;i<3;i++)
	{
		for(j=0;j<4;j++)
		{
			
			if(max<arr[i][j])
			{
				max = arr[i][j];
			}
			printf("%d ",arr[i][j]);
			
		}
		printf("\n");
		
	}
	printf("%d  %d  %d",max,i,j);
	
	return 0;
}

4函数

函数作用

避免代码冗长

可以实现模块化设计

可以按功能进行划分

函数要先定义后使用跟 变量是一样的

函数的定义

三要素

函数名 (体现功能)

参数

返回值

无参数类型函数

站空位

案例

#include<stdio.h>
// 输入3个数 输出相应水仙花数  形式参数和实际参数 名 类型 值相同,地址不相同

// 生命周期: 被调用的时候才为 形式参数 申请内存空间,调用完后又被内存释放了;
int add(int data1, int data2,int data3) //形式参数
{
	int ret;
	ret = data1*data1*data1+data2*data2*data2+data3*data3*data3;
	
	return ret;
	
}

int main()
{
	int a;
	int b;
	int c;
	int ret;
	puts("请输入三个数");
	scanf("%d%d%d",&a,&b,&c);//实际参数
	ret = add(a,b,c);
	printf("%d ",ret);
	
	return 0;
}

#include<stdio.h>

int max(int x,int y)
{
	int ret;
	if(x<y)
	{
		ret = y;
		
	}else if(x>y)
	{
		ret = x;
	}else if(x=y)
	{
		ret = x =y;
	}
	//ret =a<b?a:b;//三目运算符
	return ret;
	
}

int main()
{
	int x;
	int y;
	puts("请输入两个数");
	scanf("%d%d",&x,&y);
	max(x,y);
	printf("最大数位%d ",max(x,y));
	
}

函数的嵌套

案例

#include<stdio.h>
// 四个数比较大小 用函数实现
int max(int a,int b)
{
	int ret;
	ret = a>b?a:b;
	return ret;
	
}
int bigdata(int a, int b, int c, int d)
{
	int data;
	data = max(a,b);
	data = max(data,c);
	data = max(data,d);
	return data;
}


int main()
{
	int a;
	int b;
	int c;
	int d;
	int big;
	
	puts("please Input nums:");
	scanf("%d%d%d%d",&a,&b,&c,&d);
	big = bigdata(a,b,c,d);
	printf("最大数为%d",big);
	
	return 0;
}

这里用到了函数的嵌套法则,函数嵌套就是,当我们调用一个函数时,在这个函数又调用了其他的函数。

函数的递归

函数递归就是 在函数嵌套的过程中 把自己也嵌套进去了,这种情况容易出现死循环,递归不经常使用

#include<stdio.h>
//用递归的方式看年龄
int getAge(int num)
{
	int ret;
	if(num ==1)
	{
		ret =10;
	}else{
		ret = getAge(num-1)+2;
		//现在年龄就是 上一个人的年龄+2 num-1就是上一个 
	}
	return ret;
	
}

int main()
{
	int num;
	int age;
	int agge;
	puts("please Input You need num age :");
	scanf("%d",&num);
	agge = getAge(num);
	
	printf("%d的年龄是%d",num,agge);
	
	
	
	return 0;
}

以上是递归按照规律查找同学年龄的方式,

用递归求阶乘
#include<stdio.h>
#include<stdlib.h>
//递归求阶层
int getjiecheng(int a)
{
	int ret;
	if(a>=17)
	{
		printf("越界");
		exit(-1); //直接退出程序 调用stdlib.h 库文件
	}
	if(a == 1)
	{
		ret = 1;
		
	}else{
		ret = getjiecheng(a-1)*a;
	}
	
	return ret;
}

int main()
{
	int a;
	int jiecheng;
	puts("please Input You Need Numm");
	scanf("%d",&a);
	jiecheng = getjiecheng(a);
	printf("%d的阶乘是%d",a,jiecheng);
	
	return 0;
}

用递归的方式求取 阶乘。

$$
阶乘 a! = (a-1)*a*`````````*(a-n);;;; a! = (a-1)!*a
$$

数组作为函数的参数

数组形参

根据您提供的代码和错误信息,出现了函数调用参数数量不匹配的错误。根据错误信息看,您在函数调用printArr()中没有传递足够的参数,但是函数printArr()在函数定义处声明了两个参数(int arr[]和int len)。

为了解决这个错误,您需要在函数调用printArr()中传递正确的参数数量,以匹配函数定义处的参数。根据函数定义处的声明,您需要传递一个int类型的数组和一个int类型的长度参数。

#include <stdio.h>

void printArr(int arr[], int len) {
    int i;
    for (i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int len = sizeof(arr) / sizeof(arr[0]);

    prin

arr 使用 sizeof 运算符的错误。警告信息指出 sizeof(arr) 返回的是指针类型 int * 的大小,而不是数组的大小。

在C语言中,当我们将数组传递给函数时,数组参数实际上是以指针的形式传递的,因此在函数内部无法通过 sizeof 运算符获取数组的大小。

#include<stdio.h>
//数组传参,形式参数的些许小问题。
// 传送过来的只是一个 地址  一个数组的首地址,数组首元素的地址
//形参中没有数组这个概念,即使标注数组的大小,也是无效的
//指针变量占 8个字节 
void printArr(int arr[],int len)  	
{
	int i;
	printf("大小%d \n",len);
	for(i=0;i<len;i++)
	{
		printf("%d ",arr[i]);
		
	}
}

int main()
{
	int len;
	
	int arr[] = {1,2,3,4,5,6,7};
	len = sizeof(arr)/sizeof(arr[0]);
	
	
	printf("arr 的大小是%d\n",sizeof(arr));
	printf("arr 元素有%d个\n",len);
	printf("arr 单个元素大小是%d\n",sizeof(arr[0]));
	printArr(arr,len);//数组名 代表数组首地址
	printf(&arr[0],len); //第一个元素的地址也是 数组首地址
	return 0;
}

数组实参

变量名 当作函数的实际参数
#include<stdio.h>
void dataa(int data)
{
	data = data +1000;
	printf("address %p, data %d",&data,data);
}

int main()
{
	int data;
	data=10;
	printf("address %p %d \n",data);
	dataa(data);
	return 0;
}

当变量 为函数的实际参数的时候 只是将函数中变量的值进行传递,而地址并没有传递

这两个数据的地址是不相同的

数组名 当作函数实际参数

当数组名作为函数的实际参数的时候,不仅能够进行函数间的数值传递,而且能够将地址进行传递

#include<stdio.h>
int dataa(int arr[])
{
	arr[0]=arr[0]+100;
	printf("address %p ,data %d",&arr[0],arr[0]);
	
}
int main()
{
	int arr[] = {1,33,4,23,35};
	
	dataa(arr);
	printf("\naddress %p ,data %d",&arr[0],arr[0]);
	
	return 0;
}

地址是一样的

数组实战之计算不同班级平均分

在C语言中,当我们在函数调用中传递数组参数时,我们只需要传递数组名,而不需要使用[]符号。

为了解决这个问题,您需要在调用函数initArr()时,将数组名arr1作为参数传递,而不需要使用[]

#include<stdio.h>
//计算两个班级 的平均分 5 10
void initArr(int arr[],int len)
{
	int i;
	
	for(i=0;i<len;i++)
	{
		printf("请输入第%d个同学的成绩\n",i+1);
		scanf("%d",&arr[i]);	
	}
	puts("dome\n");
	
}
void printArr(int arr[],int len)
{
	int i;
	printf("总人数是%d\n",len);
	for(i=0;i<len;i++)
	{
		printf("第%d同学成绩是%d\n",i+1,arr[i]);
		
	}
	puts("\ndome");
}
float getpinjunfen(int arr[],int len)
{
	int i;
	float pingjunfen;
	int sum;
	for(i=0;i<len;i++)
	{
		sum = sum+arr[i];
	}
	pingjunfen = (float)sum/len;
	return pingjunfen;
	
}

int main()
{
	int arr1[5];
	int arr2[10];
	float aa;
	float bb;
	int len1 = sizeof(arr1)/sizeof(arr1[0]);
	int len2 = sizeof(arr2)/sizeof(arr2[0]);
	
	
	initArr(arr1,len1);
	initArr(arr2,len2);
	
	printArr(arr1,len1);
	printArr(arr2,len2);
	
	aa = getpinjunfen(arr1,len1);
	bb = getpinjunfen(arr2,len2);
	printf("1平均分为%2.2f\n2平均分为%2.2f",aa,bb);
	printf("\n dome \n");
	
	
	
	
}

二维数组 和 函数 的概念 与实战

二维数组命名规范
int arr[][]; //错误的
int arr[][2] ={1,2,3,4,5,2,2,3,3,1,}; // 正确
int arr[2][3]// 正确
    //二维数组命名 除了第一个维度不需要规定边界 第二维度,第n维度斗需要规定边界
    // 注意两个点  二维数组的类型; 二维数组有多少个一维数组

二维数练习题
有3x4的矩阵,初始化并输出,然后求最大值并输出
#include<stdio.h>
// 输出4X5矩阵 求最大值
void initArr();

void printArr();
int getMax();

int main()
{
	int arr[4][5];
	int max;
	int hang = 4;
	int lie = 5;
	
	
	initArr(arr,hang,lie);
	printArr(arr,hang,lie);
	max = getMax(arr,hang,lie);
	printf("最大值为%d",max);
	
	return 0;
}

//数组初始化
void initArr(int arr[][5],int hang,int lie)
{
	int i;
	int j;
	for(i=0;i<hang;i++)
	{
		for(j=0;j<lie;j++)
		{
			printf("第%d行,第%d列",i+1,j+1);
			scanf("%d",&arr[i][j]);
			getchar();
			
		}
		
	}
	printf("初始化完成\n");
	
}
//打印二维数组各个元素
void printArr(int arr[][5],int hang,int lie )
{
	int i;
	int j;
    //遍历数组
	for(i=0;i<hang;i++)
	{
		for(j=0;j<lie;j++) //当把j换成i的时候,会越界
		{
			printf("%d  ",arr[i][j]);
		}
		printf("\n");
	}
	
}
//获取最大值
int  getMax(int arr[][5],int hang,int lie)
{
	int i;
	int j;
	int max;
	for(i=0;i<hang;i++)
	{
		for(j=0;j<lie;j++)
		{
			if(max<arr[i][j])
			{
				max =arr[i][j];
			}
		}
	}
	return max;
}

全局变量,外部变量,局部变量区别

#include<stdio.h>
int a = 10;
int b = 12;// 全局变量
void fun1()
{
	printf("%d\n",a);
	printf("%d\n",b);
}
int c = 1;
int d = 2;  //外部变量
void fun2()
{
	printf("%d\n",c);
	printf("%d\n",c);
}

int main()
{
	int aaa =11;
	int bbb= 111; //局部变量
	fun1();
	fun2();
	printf("%d\n",a);
	printf("%d\n",b);
	return 0;
}

程序是按照自上而下 的顺序进行编译的

$$
全局变量 是指 变量不在函数中 并且 在最上面, 外部变量 上面有其他的函数,而且 又不在函数内 局部变量 在函数中的变量
$$

全局变量实战
#include<stdio.h>
//计算一个班级学生的平均分

int max;
int min;
float getAver(int arr[],int len)
{
	int i;
	int sum =0 ;
	
	max = min = arr[0];//确定 取值范围 初始化
	
	for(i=0;i<len;i++)
	{
		printf(" %d ",arr[i]);
		if(max<arr[i])
		{
			max = arr[i];
		}
		if(min>arr[i])
		{
			min=arr[i];
		}
		sum = sum +arr[i];
		
	}
	return (float)sum/len;
	
}


int main()
{
	int arr[] = {22,33,3};
	int len;
	len = sizeof(arr)/sizeof(arr[0]);
	float aver;
	aver = getAver(arr,len);
	
	printf("最高分%d\n最低分%d\n平均分%2.2f\n",max,min,aver);
	//printf(); 有优先级 先执行函数,后执行变量
	
	return 0;
}

作业

在C语言中,当我们声明函数的参数时,不需要使用分号(;)来表示前向声明。

void init(int a; int b)  //这里的;改为,
{
    
}
void initt(int a,int b)
{
    
}

#include <stdio.h>

void initArr(int arr[], int len) {
    // 在这里初始化数组
}

int main() {
    int arr[10];
    int len = sizeof(arr) / sizeof(arr[0]);

    initArr(arr, len);

    // 在这里使用数组

    return 0;
}
1,作业代码
#include<stdio.h>
/*
	作业:输入十个数
	求最大值,及下标
*/
void initArr(int arr[],int len)
{
	int i;
	printf("请输入10个数据\n");
	for(i=0;i<len;i++)
	{
		scanf("%d",&arr[i]);
		
		
	}
	printf("\n初始化完成\n");
}

void printArr(int arr[], int len)
{
	int i;
	for(i=0;i<len;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n打印结束");
	
}
int getMax(int arr[],int len)
{
	int i;
	int j;
	int max=arr[0];
	for(i=0;i<len;i++)
	{
		if(max<arr[i])
		{
			max = arr[i];
			j=i;
		}
	}
	
	return max;
	
}

int getj(int arr[],int len)
{
	int i;
	int j;
	int max=arr[0];
	for(i=0;i<len;i++)
	{
		if(max<arr[i])
		{
			max = arr[i];  //已经得到数组中最大的值
							//但是下标问题,还没有解决
		}
		
	}
	//再次遍历数组 寻找到与max的元素,这时候就可以找到下标了
	for(i = 0;i<len;i++)
	{
		if(max == arr[i])
		{
			j = i+1;
		}
		
	}
	
	return j;
	
}


int main()
{
	int arr[10];
	int len;
	int max;
	int j;
	len =sizeof(arr)/sizeof(arr[0]);
	
	initArr(arr,len);
	printArr(arr,len);
	max= getMax(arr,len);
	j= getj(arr,len);
	printf("最大值为%d 下标为%d",max,j);
	return 0;
}

需求:输入10个数,然后输出最大值,以及最大值的下标。

定义一个有10个元素的数组,通过数组初始化,输出数组的元素,然后再通过 getmax()找到max

然后再找到下标。

初始化数组
void initArr(int arr[],int len)
{
	int i;
	printf("请输入10个数据\n");
	for(i=0;i<len;i++)
	{
		scanf("%d",&arr[i]);
		
		
	}
	printf("\n初始化完成\n");
}
输出数组 遍历数组
void printArr(int arr[], int len)
{
	int i;
	for(i=0;i<len;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n打印结束");
	
}
找到最大值
int getMax(int arr[],int len)
{
	int i;
	int j;
	int max=arr[0];
	for(i=0;i<len;i++)
	{
		if(max<arr[i])
		{
			max = arr[i];
			j=i;
		}
	}
	
	return max;
	
}
找到最大值的下标值

找最大值的下标值有很多种方法,这里是简单的方法,通过再次遍历数组,找出与max相等的元素,然后将其下标读取出来。直接返回

int getj(int arr[],int len)
{
	int i;
	int j;
	int max=arr[0];
	for(i=0;i<len;i++)
	{
		if(max<arr[i])
		{
			max = arr[i];  //已经得到数组中最大的值
							//但是下标问题,还没有解决
		}
		
	}
	//再次遍历数组 寻找到与max的元素,这时候就可以找到下标了
	for(i = 0;i<len;i++)
	{
		if(max == arr[i])
		{
			j = i+1;
		}
		
	}
	
	return j;
	
}
主函数
int main()
{
	int arr[10];
	int len;
	int max;
	int j;
	len =sizeof(arr)/sizeof(arr[0]);
	
	initArr(arr,len);
	printArr(arr,len);
	max= getMax(arr,len);
	j= getj(arr,len);
	printf("最大值为%d 下标为%d",max,j);
	return 0;
}

2,封装冒泡排序法

#include<stdio.h>
void initArr(int arr[],int len)
{
	int i;
	int j;
	int temp;
	for(i=0;i<len-1;i++)
	{
		for(j=0;j<len-1-i;j++)
		{
			if(arr[j]<arr[j+1]) //如果前项 <后向 就交换
			{
				temp = arr[j];
				arr[j]= arr[j+1];
				arr[j+1]=temp;
				
			}
		}
		
	}
	for(i=0;i<len;i++)
	{
		printf("%d  ",arr[i]);
	}
	for(i=0;i<len;i++)
	{
		printf("\n%p  ",&arr[i]);
	}
}
int main()
{
	int arr[] = {1,33,4,32,54,42,32,12};
	int len = sizeof(arr)/sizeof(arr[0]);
	
	initArr(arr,len);
	
	return 0;
}

封装冒泡排序法,是需要数组传参。数组传参是将地址传过去,并不只是值。

3,封装选择牌序

#include<stdio.h>
void initxuanze(int arr[],int len)
{
	int i;
	int j;
	int temp;
	for(i=0;i<len-1;i++)
	{
		for(j=i+1;j<len;j++)
		{
			if(arr[i]<arr[j])
			{
				temp = arr[j];
				arr[j] = arr[i];
				arr[i] = temp;
			}
		}
	}
	for(i=0;i<len;i++)
	{
		printf("%d ",arr[i]);
		printf("%p \n",&arr[i]);
		
	}
	
}

int main()
{
	int arr[] = {12,33,44,55,55};
	int len;
	len = sizeof(arr)/sizeof(arr[0]);
	
	initxuanze(arr,len);
	
	return 0;
}

上部分总结

初识C语言

变量 标识符

int  float   char  double  

printf()

//原样输出
//占位符输出
//可以多样输出

printf("");

scanf()

流程控制

顺序结构 选择结构 循环结构

顺序结构

数组

函数

5指针

1,初识指针

指针变量 存放地址的变量

#include<stdio.h>
int main()
{
    int *p;
    printf("");
    
    return 0;
}

#include<stdio.h>

// 指针就是地址
// 指针变量就是用来 存放 地址的变量
// 定义一个指针变量 *标识的作用 产生在定义与声明的时候
// 使用指针变量 * 运算的作用

int main()
{
	int a = 10;
	int *p; //*是标识符 告诉系统我是 指针变量 用来保存 地址
	p = &a;  //p等于 取地址 a
	
	printf("数据 %d \n",a);
	printf("地址 %p \n",&a);  
	printf("数据 %d \n",*(&a)); //*是取值运算符,将内存地址的数据取出来。
	printf("指针变量访问数据 %d \n",*p);
	printf("指针变量 %d",sizeof(*p));
	
	return 0;
}

指针变量的类型

$$
不同的类型 决定不同访问空间的大小 int *pdata; char *pchar 决定增量
$$

#include<stdio.h>
int main()
{
	int a = 0x1234;
	int *p 	 = &a;
	char *c = &a;
	printf("地址是%p  数据是%d\n",p,*p);
	printf("地址是%p  数据是%d\n",c,*c);
	printf("地址是%p  数据是%x\n",p,*p);
	printf("地址是%p  数据是%x\n",c,*c);
	
	printf("地址是%p  数据是%x\n",++p,*p);
	printf("地址是%p  数据是%x\n",++c,*c);
	
	return 0;
}

通过不同的类型 ++p 增量也会不同

访问变量 通过指针访问变量 通过变量名访问变量

警告,存在将int*类型的指针赋值给char*类型的指针的不兼容指针类型警告。

指针使用的场景

用函数封装两数交换的方法
#include<stdio.h>

//两个数交换
void jiaohuan(int *pdata1,int *pdata2)
{
	int temp;
	
		temp = *pdata1;
		*pdata1 = *pdata2;
		*pdata2 = temp;
	
	printf("%d   %d\n",*pdata1,*pdata2);
}

int main()
{
	int data1;
	int data2;
	printf("交换前\n");
	scanf("%d%d",&data1,&data2);//输入两个数
	printf("%d   %d\n",data1,data2);
	jiaohuan(&data1,&data2);
	printf("交换后\n");
	printf("%d   %d\n",data1,data2);
	
	return 0;
}
指向指针的固定区域

错误

C语言中,整数常量的后缀用于指定整数的类型,但是FE55不是有效的后缀。

如果您想要将一个具体的地址赋值给指针变量,您需要使用合适的地址表示方法。

#include <stdio.h>

int main() {
    int *p = (int *)0x61FE55;  // 使用16进制表示地址

    // 使用指针进行操作
    *p = 10;
    printf("%d\n", *p);

    return 0;
}

#include<stdio.h>
int main()
{
	int a = 11;
	printf("0x%p  \n",&a); //打印a的地址
	int *p = (int *)0x000000000061FE55;
	printf("0x%p  \n",p); //打印上面的地址
}

#include<stdio.h>
int main()
{
	int a = 11;
	printf("0x%p  \n",&a); //打印a的地址  volatile 防止系统优化
	volatile unsigned int *p = (volatile unsigned int *)0x000000000061FE55;
	printf("0x%p  \n",p); //打印上面的地址
}

3,作业 封装 输入三个数

$$
int *p //定义指针变量 int a ; printf("%d",*(&a));
$$

#include<stdio.h>
//输入3个数 以大到小顺序输出
void changData(int *data1,int *data2,int *data3)
{
	int a;
	int b;
	int c;
	int temp;
	printf("取值 %d  %d  %d\n",*data1,*data2,*data3);
	printf("取值 %p  %p  %p\n",*data1,*data2,*data3);
	printf("取地址 %p  %p  %p\n",&data1,&data2,&data3);
	if(*data1<*data2)
	{
		temp = *data1;
		*data1 = *data2;
		*data2 = temp;
	}
	if(*data1<*data3)
	{
		temp = *data1;
		*data1 = *data3;
		*data3 = temp;
		
	}
	if(*data2<*data3)
	{
		temp = *data2;
		*data2 = *data3;
		*data3 = temp;
		
	}
	printf("取地址 %d  %d  %d\n",&data1,&data2,&data3);
	printf("取值 %p  %p  %p\n",*data1,*data2,*data3);
	printf("取地址 %p  %p  %p\n",&data1,&data2,&data3);
	printf("%d  %d  %d\n",*data1,*data2,*data3);
	
}

int main()
{
	int data1;
	int data2;
	int data3;
	
	
	scanf("%d%d%d",&data1,&data2,&data3);
	printf("原来的顺序\n");
	printf("%d  %d  %d\n",data1,data2,data3);
	printf("交换后\n");
	
	changData(&data1,&data2,&data3);
	printf("%d %d  %d",data1,data2,data3);
	
	
	return 0;
}

2 ,通过指针迎合数组

1,定义 指针变量指向数组

#include<stdio.h>
int main()
{
	int arr[] = {1,3,2,43,2,55};
	int *p;
	int *q;
	int len ;
	int i;
	len = sizeof(arr)/sizeof(arr[0]);
	p = arr;
	q = arr;
	for(i =0;i<6;i++)
	{
		printf("%d  ",*(p+i)); //指针的偏移 偏移程度取决于 类型
	}
	
	printf("第二元素是 %d\n",*(p+1));
	printf("第一元素是 %d\n",*q);
	return 0;
}

2,通过指针引用数组元素

1,下标法

2,指针法
1,指针偏移

指针偏移 就是 指针往前移 或者往后移

#include<stdio.h>
int main()
{
	int arr[] = {1,3,2,43,2,55};
	int *p;
	int *q;
	int len ;
	int i;
	len = sizeof(arr)/sizeof(arr[0]);
	p = arr;
	q = arr;
	for(i =0;i<len;i++)
	{
		printf("%d  ",*(p+i)); //指针的偏移 偏移程度取决于 类型
	}
	printf("\n");
	p = arr; // 从新回到数组首地址  从新  遍历数组
	for(i = 0;i<len;i++)
	{
		printf("%d  ",*p++);
	}
	printf("\n");
	p = arr;
	for(i = 0;i<len;i++)
	{
		printf("%d  ",*p);
		p++;
	}
	printf("\n");
	
	printf("第二元素是 %d\n",*(p+1));
	printf("第一元素是 %d\n",*q);
	return 0;
}

2,取内容

指针和数组名 的见怪不怪

指针当作数组名 下标法访问 朝花夕拾

#include<stdio.h>

int main()
{
	int arr[5]= {11,3,23,4,5};
	int *p = arr;
	int i;
	int len ;
	len = sizeof(arr)/sizeof(arr[0]);
	printf("%d  \n",sizeof(arr));// 4 字节 x 5 = 20
	printf("%d  \n",len);
	printf("%d  \n",sizeof(int *)); //用8个字节表示一个地址
	printf("%d  \n",sizeof(char*));
	printf("%d  \n",sizeof(float*));
	
	for(i = 0;i<len;i++)
	{
		printf("%d ",arr[i]);
		
	}
	putchar('\n');
	for(i = 0;i<len;i++)
	{
		printf("%d ",*(p+i));
	}
	putchar('\n');
	for(i = 0;i<len;i++)
	{
		//printf("%d ",*arr++); *p 是指针变量  
		// arr++ 是指针常量,编译不过。
		printf("%d ",*(arr+i)); 
	}
	putchar('\n');
	for(i = 0;i<len;i++)
	{
		printf("%d ",*(&arr[i]));
		
	}
	
	return 0;
}

3,两种方法 效率对比

函数与指针 与数组的结合

封装冒泡
#include<stdio.h>
//用指针封装 冒泡排序法
void initArr(int *arr,int len)
{
	int i;
	puts("初始化开始");
	for(i = 0;i<len;i++)
	{
		scanf("%d",arr++);
	}
	puts("初始化结束");
}

void printArr(int *arr,int len)
{
	int i;
	for(i = 0;i<len;i++)
	{
		printf("%d  ",*arr++);
	}
	
}

void maopaoArr(int *arr,int len)
{
	int i;//交换的轮数 
	int j;//每一轮交换的次数
	int temp;//临时变量
	for(i=0;i<len-1;i++)
	{
		for(j=0;j<len-i-1;j++)
		{
			if(*(arr+j)>*(arr+j+1))
			{
				//printf("aa ");
				temp = *(arr+j);
				*(arr+j) = *(arr+j+1);
				*(arr+j+1) = temp;
				
			}
		}
		
	}
	
}

int main()
{
	int arr[5];
	int len;
	len = sizeof(arr)/sizeof(arr[0]);
	puts("程序开始");
	initArr(arr,len);
	printArr(arr,len);
	maopaoArr(arr,len);
	printf("\n");
	printArr(arr,len);
	
	return 0;
}

封装选择
#include<stdio.h>
#define mian main
// 用指针封装选择排序
void initArr(int *arr,int len)
{
	int i;
	for(i=0;i<len;i++)
	{
		scanf("%d",arr++);
	}
	
}

void printArr(int *arr,int len)
{
	int i;
	for(i=0;i<len;i++)
	{
		printf("%d ",*arr++);
	}
}

void xuanze(int *arr,int len)
{
	int i;
	int j;
	int temp;
	for(i = 0;i<len-1;i++)
	{
		printf("bb\n");
		for(j=i+1;j<len;j++)
		{
			printf("aa\n");
			if(*(arr+j)>*(arr+i))
			{
				
				temp = *(arr+i);
				*(arr+i) = *(arr+j);
				*(arr+j) = temp;
				
			}
			
		}
	}
	
	
}

int mian()
{
	int arr[5];
	int len ;
	len = sizeof(arr)/sizeof(arr[0]);
	initArr(arr,len);
	printArr(arr,len);
	xuanze(arr,len);
	printf("\n");
	printArr(arr,len);
	
	return 0;
}

封装交换
#include<stdio.h>
// 数组 翻转
void initArr(int *arr ,int len)
{
	int i;
	for(i =0;i<len;i++)
	{
		scanf("%d",arr++);
	}
	
}

void printArr(int *arr,int len)
{
	int i;
	for(i =0;i<len;i++)
	{
		
		printf("%d ",*arr++); //*取值 运算符(在这里是)
	}
	
}

void fanzhuan(int *arr,int len)
{
	int i;
	int j;
	int temp;
	for(i = 0;i<len/2;i++)
	{
		j = len-1-i;
		temp = *(arr+i);
		*(arr+i) = *(arr+j);
		*(arr+j) = temp;
		
	}
	
}


int main()
{
	int arr[5];
	int len ;
	len = sizeof(arr)/sizeof(arr[0]);
	printf("%d  \n",len);
	initArr(arr,len);
	printArr(arr,len);
	printf("dome\n");
	
	fanzhuan(arr,len);
	printArr(arr,len);
	
	return 0;
}

3,二维数组地址的认知

1,二维数组 第一维数组的每一个元素都是一个数组。

$$
int arr[][]; arr[0]+1; //第一个数组的第二个元素 arr[0] //子数组的名字 也是子数组的首地址
$$

$$
arr[0]+1; 指针指向子数组的时 向后偏移了一位 指向了子数组首地址 的后一位
$$

#include<stdio.h>
int main()
{
	int arr[3][4]={{1,2,33,2},{33,11,44,55},{5,32,12,345}};
	
	printf("父数组的地址是:%p  偏移%p\n",arr,arr+1);  //父亲偏移了16
	printf("子数组的地址是:%p  偏移%p\n",arr[0],arr[0]+1);//孩子偏移了4
	printf("子数组的地址是:%p  偏移%p\n",*(arr+0),*(arr+0)+1);//孩子偏移了4
	
	
	return 0;
}

二维数组 地址写法应用
int a[3][4];
a;  //表示
a[0]+1;//第0行第1列 *(a+0)+1
a[1], *(a+1);
&a[1] a+1
a[2],*(a+2);
a[1]+2  ,*(a+1)+2,  &a[1][2];  //取1 2 地址
*(a[1][2]) ,*(*(a+1)+2), *(&a[1][2] ) ,*(a[1]+2) ,a[1][2] //取1 , 2值

4,数组指针

1,初识数组指针

int (*p) 指针变量

*()取值运算符

int arr[3][4] = ;
int (*p)[4];   //定义一个数组指针
p = arr;

能不能定义一个指针,指针偏移的时候 也偏移对应的数组 ​ // 数组指针 定义一个指针 指向数组 ​ // 数组指针才是真正等同于 二维数组的名字

#include<stdio.h>
int main()
{
	int arr[3][4]={{1,3,34,2},{21,34,5,54},{21,5,54,22}};
	int i;
	int j;
	for(i = 0;i<3;i++)
	{
		for(j = 0;j<4;j++)
		{
			printf("%d ",arr[i][j]);
			
			
		}
		
		printf("\n");
	}
	putchar('\n');
	for(i = 0;i<3;i++)
	{
		for(j = 0;j<4;j++)
		{
			printf("%d ",*(*(arr+i)+j));
			
			
		}
		
		printf("\n");
	}
	
	putchar('\n');
	// 能不能定义一个指针,指针偏移的时候 也偏移对应的数组
	// 数组指针  定义一个指针 指向数组
	// 数组指针才是真正等同于 二维数组的名字
	
	int (*p1)[4];  //数组指针定义方式
	p1 = arr;
	for(i = 0;i<3;i++)
	{
		for(j = 0;j<4;j++)
		{
			printf("%d ",*(*(p1+i)+j));
			
			
		}
		
		printf("\n");
	}
	
	return 0;
}

2,数组指针和二维数组的配合

#include<stdio.h>
// 用户输入行列的值 然后输出 行列对应的数字
void initscanfhanglie(int *hang,int *lie )
{
	puts("请输入行和列");
	scanf("%d%d",hang,lie);
	puts("输入完成");
}

int  getdata(int (*p)[4],int hang,int lie)
{
	int data;
	data = *(*(p+hang)+lie);
	
	return data;
	
}

int main()
{
	int arr[3][4] = {{11,3,4,2},{33,55,66,22},{334,65,7,90}};
	//用户 行列数
	int hang,lie;
	int data;
	initscanfhanglie(&hang,&lie);
	
	//获取 行列 对应的数字
	data = getdata(arr,hang,lie);
	printf("%d  %d  %d",hang,lie,data);
	
	return 0;
}

5,函数指针

1,定义函数指针

函数名就是地址 数组名也是地址

在程序中定义一个函数,编译系统为函数代码分配存储空间,,这段存储空间的起始地址

(入口地址)就是这个函数的指针。

2,定义函数指针变量

int a;
int *p;

char c;
char *p;

int (*p)(int a,int b); ///定义函数指针
int *p(int a,int b);
函数指针 的好用的地方
根据程序运行过程的不同情况,调用不同的函数

练习题 输入a b 根据 1,2,3模式 1,求最大值 2,求最小值 3,求和

#include<stdio.h>
#include<stdlib.h>
//输入 两个数 有3个模式 1最大值 2最小值 3求和
int getMax(int data1,int data2)
{
	int max;
	max = data1>data2?data1:data2;
	return max;
}
int getMin(int data1,int data2)
{
	int min;
	min = data1<data2?data1:data2;
	return min;
}
int getSum(int data1,int data2)
{
	int sum;
	sum = data1+data2;
	return sum;
}
int getmode(int data1,int data2, int (*pfunc)(int,int))
{
	int ret ;
	ret = pfunc(data1,data2);
	
	return ret;
}


int main()
{
	int a;
	int b;
	int ret;
	printf("请输入两个数\n");
	scanf("%d%d",&a,&b);
	putchar('\n');
	printf("请输入对应的模式 1 最大值,2最小值,3求和\n");
	int cmd;
	int (*pfunc)(int,int);
	scanf("%d",&cmd);

	switch(cmd)
	{
		case 1:
			pfunc =getMax;
		break;
		case 2:
			pfunc = getMin;
		break;
		case 3:
			pfunc =getSum;
		break;
		default:
			printf("错误\n");
			exit(-1);
		break;
	}
	ret = getmode(a,b,pfunc);
	printf("输入的两个数为%d  %d  \n %d结果为%d",a,b,cmd,ret);
	
	return 0;
}
回调函数

指针数组

一个数组中的元素 均为指针类型的数据 成为指针数组。

指针数组中每一个 元素存放一个地址,相当于一个指针变量

int (*p)[2];  //这个是指向一推数组的指针变量
int a;
int b;
int c;
int d;

int *p[3] = {&a,&b,&c}; //指针数组
int *p[2][3];

3,使用函数指针

1,函数调用概念和变量一样

直接访问(变量名,函数名)

间接访问 (指针 函数指针)

4,指针数组 和数组指针的区别

数组指针(pointer to an array):

  • 数组指针指的是一个指针,它指向一个数组的内存块。

  • 数组指针的声明包括指针名和所指向数组的元素类型,但不包括数组大小。

  • 通过解引用数组指针加上索引来访问数组中的元素。

  • 示例代码

  • int (*arr)[10];//声明一个指针,指向一个具有10个元素的数组
    int arr[10];  //数组
    
    int (*ptr)[10];  // 声明一个指针,指向包含10个整数元素的数组
    int arr[10];     // 声明一个包含10个整数元素的数组
    ptr = &arr;      // 将数组的地址赋值给指针
    (*ptr)[0] = 5;   // 通过指针访问数组元素,并赋值

    指针数组(array of pointers):

    • 指针数组是一个数组,在数组中的每个元素都是指针,指向不同的内存块。

    • 指针数组的声明包括指针数组的名字和每个指针所指向的数据类型。

    • 指针数组的每个元素都可以指向不同类型的数据(指针类型除外)。

    • 示例代码:

    • int *arr[5];   // 声明一个包含5个指针元素的指针数组
      int num1 = 5, num2 = 10, num3 = 15;
      arr[0] = &num1;  // 指针数组的第一个元素指向 num1
      arr[1] = &num2;  // 指针数组的第二个元素指向 num2
      arr[2] = &num3;  // 指针数组的第三个元素指向 num3
      *arr[0] = 7;     // 通过指针数组访问并修改所指向的数据
    • 数组指针指向一个数组的内存块,而指针数组是一个包含指针的数组。

    • 数组指针的声明形式为 elementType (*ptr)[size],而指针数组的声明形式为 elementType *arr[size]

    • 数组指针可以通过解引用和索引操作访问数组元素,指针数组可以通过索引操作访问每个指针所指向的对象。

    • 数组指针和指针数组的应用场景和使用方式有所不同,具体取决于程序的需求。

5,指针函数

指针函数 就是 返回值 是指针变量的函数

int *a(int a,int b)
{
    int *p;
    p = &a;
    return p;
    
}

#include<stdio.h>
int *getPosPerson(int pos,int (*arr)[4])
{
	int *p;
	p = (int *)arr+pos;
	return p;
}

int main()
{
	int arr[3][4] = {
		{11,33,54,43},
		{80 ,94,76,98},
		{79,80,76,45},
	};
	int pos;
	int *ppos;
	
	printf("请输入学生的学号0 1 2\n");
	scanf("%d",&pos);
	ppos = getPosPerson(pos,arr);
	for(int i = 0;i<4;i++)
	{
		printf("%d  ",*ppos++);
	}
	return 0;
}

6,二级指针

#include<stdio.h>
int main()
{
    int data1 = 100;
    int *p;
    p = &data;
    printf("%p ",p);
    int **p2;
    p2 =&p;
    printf("  p的地址是%p  ",p2);
    return 0;
}

#include<stdio.h>
int main()
{
	int data = 100;
	int *p = &data;  //P保存的是data的地址;
	printf("data的地址是%p \n",p);
	int**p2=&p;
	p2 =&p;
	int a = (int )**p2;
	printf("pp2 保存的是 p的地址 %p \n",p2);
	printf("*p2 是%p \n",*p2);
	printf("**p2 是%p ,",**p2);
	
	return 0;
}

1,野指针

定义一个 指针变量 如果 我们没有给这个指针变量 要保存的地址 那这个指针变量 就是一个野指针。系统会随机为这个指针变量分配保存的地址。

int *p; //p后面没有要保存的地址,所以这个指针变量就是野指针

6,字符串

1,字符串就是字符数组

1,定义的几种格式

#include<stdio.h>
int main()
{
    int *char = "holle word";
}

#include<stdio.h>
int main()
{
	//比较蠢的方式
	char arr[]={'f','e','e','r','w','e','r','f','s'};
	//比较人性的方式
	char arr1[]="hello";
	//比较常用的方式
	char *arr2 = "困困";
	
	
	int len =sizeof(arr)/sizeof(arr[0]);
	int i;
	for(i=0;i<len;i++)
	{
		printf("%c",arr[i]);
	}
	putchar('\n');
	
	printf("%s\n",arr1);
	printf("%s\n",arr2);
	
	return 0;

}

#include<stdio.h>
#include<string.h>


int main()
{
	int a;
	int data1[] = {1,2,3,4,5};
	char cdata[] = {'h','o','l','l','e'};
	char cdata1[] = "holle";
	a = len (data1);
	printf("%d ",a);
	
	return 0;
}

2,和整型数组在存储上的区别

字符串多了一个 ‘ \0’ 长度多了一个字节

#include<stdio.h>
int main()
{
	//字符串和 字符数组的区别  
		//字符串存储 比字符数组多了‘\0’结束标志
	
	int data1[] = {1,2,3,4,5};
	char data2[] = {'f','e','e','r','t','h','e','n','\0'};
	
	char data3[] = "feerthen";
	char *data4 = "feerthen";
	int len;
	int len1;
	int len2;
	int len3;
	len = sizeof(data1)/sizeof(data1[0]);
	len1 = sizeof(data2)/sizeof(data2[0]);
	len2 = sizeof(data3)/sizeof(data3[0]);//字符串多了一个'\0'结束标志
	len3 = sizeof(data4)/sizeof(data4);
	
	printf("%d \n",len);
	printf("%d \n",len1);
	printf("%d \n",len2);
	printf("%d \n",len3);
	
	return 0;
}

3,sizeof和strlen的区别

sizeof() 计算字符串所有的元素(包括''\0')

strlen() 只计算有效部分

#include<stdio.h>
#include<string.h>
int main()
{
	char data[] = "hello";
	printf("data sizeof :%d\n",sizeof(data));
	printf("data strlen :%d\n",strlen(data));
	
	return 0;
}

4,动态开辟字符串

1,malloc 动态开辟内存空间

free(p); //把前面malloc申请的空间释放掉。防止内存泄露,防止悬挂指针。

realloc(p,newlen); //realloc 是在 malloc分配地址不够的情况下 进行扩容地址

memset(p,'\0',12); //把p里面的12 个字节全部初始化成 ‘\0’

p = (char*)malloc(12); //动态开辟12个字节

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
	char *p; //野指针
	p = (char *)malloc(1);// p有了具体的内存指向
	*p = 'c';
	
	free(p);	//把前面malloc申请的空间释放掉。防止内存泄露,防止悬挂指针。
	
	printf("p的字符是%c\n",*p);
	printf("%p\n",p);
	
	p = (char*)malloc(12); //动态开辟12个字节
	
	printf("扩容前的地址%x\n",p);
	int len = strlen("jfiopeqqp12345321421421414126678");
	int newlen = len-2+1;
	
	realloc(p,newlen); //realloc 是在 malloc分配地址不够的情况下 进行扩容地址
	
	memset(p,'\0',12); //把p里面的12 个字节全部初始化成\0
	
	printf("扩容后的地址%x\n",p);
	
	
	
	p = strcpy(p,"jfiopeqqp12345321421421414126678");
	puts(p);
	
	//printf("%s",p);
	
	return 0;
}

5,几种字符串常用的API

strcpy()

char* copy(char*p,char*str)
{
	if(p == NULL||str == NULL)
		return NULL;
	char* ber;
	ber =p;
	while(*str != '\0')
	{
		*p = *str;
		p++;  //指针偏移
		str++;
	}
	*p = '\0';//循环结束时+一个'\0'结束标志
	return ber;
}
char* copy1(char* p,char*str)
{
	if(p == NULL||str == NULL)
		return NULL;
	char* ber;
	ber =p;
	while(*str != '\0')
		*p++ = *str++;
	*p = '\0';//循环结束时+一个'\0'结束标志
	return ber;
	
}
char* copy2(char* p,char*str)
{
	if(p == NULL||str == NULL)
		return NULL;
	char* ber;
	ber =p;
	while((*p++=*str++) != '\0');
		*p = '\0';//循环结束时+一个'\0'结束标志
	return ber;
}

strncopy()

这种方式有点bug

char* ncopy(char*p,char*str,int count)
{
	if(p == NULL||str == NULL)
		return NULL;
	char* ber;
	ber =p;
	while(*str != '\0',count>0)
	{
		*p = *str;
		p++;  //指针偏移
		str++;
		count--;
	}
	//当我们字节的值大于所需要拷贝内容的字节时,用这个
	if(count >0)
	{
		while(count>0)
		{
			count--;
			*p++ = '\0';
		}
		return ber;
	}
	*p = '\0';//循环结束时+一个'\0'结束标志
	return ber;
}

这种方式考虑就很全了

当 count 大于要复制的内容时,后面的字节我不知道有什么东西,有可能会产生不可估量的影响,所以要对后面剩余的字节进行填充。

当 count 小于复制内容时,会出现越界,所以我们要当count等于0时 给一个结束标志。

char* ncopy1(char*p,char*str,int count)
{
	if(p == NULL||str == NULL)
		return NULL;
	char* ber = p;
	while(count && (*p++ = *str++)) 
		count--;
	if(count)
		while(--count)
			*p++='\0';
	return ber;
	
}

dst :destination的缩写 src:source的缩写 strcpy : string copy

char* strcpy(char*dst, const char *src)
{
	if ((dst == NULL) || (src == NULL))
		return NULL;
	char *ret = dst;  //[1]
	while ((*dst ++= *src++) != '\0');   //[2]
		return ret;   //[3]
}
const :防止在赋值的给dst的时候发生改变;
ret :return ,返回的是dst的首地址。

while ((*dst ++= *src++) != ‘\0’); 当 *src =‘\0’ 的时候为false while()结束。

*dst ++= src++的时序问题 首先:dst= *src 然后:dst++和src++顺序不定,不同的编译器可能顺序不同 ———————————————— 版权声明:本文为CSDN博主「七 六 伍」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:阅读源码之——strcpy()函数源码解读(学习)_strcpy函数源码_七 六 伍的博客-CSDN博客

assert() 断言函数

assert() 他是一个宏 他的作用就是用来 测试 程序是否有问题 偶尔用几下

如果用完可以用 #define NDEBUG 让assert()失效

**所在头文件:<assert.h>*

*函数原型:void assert (int expression);*

*参数:expression即要检测的表达式*

*返回值:无返回值*

“断言”在语文中的意思是“断定”、“十分肯定地说”,在编程中是指对某种假设条件进行检测,如果条件成立就不进行任何操作,如果条件不成立就捕捉到这种错误,并打印出错误信息,终止程序执行。

assert() 会对表达式expression进行检测:

    如果expression的结果为 0(条件不成立),那么断言失败,表明程序出错,assert() 会向标准输出设备(一般是显示器)打印一条错误信息,并调用 abort() 函数终止程序的执行。

    如果expression的结果为非 0(条件成立),那么断言成功,表明程序正确,assert() 不进行任何操作。

———————————————— 版权声明:本文为CSDN博主「桦秋静」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:assert断言函数简介_assert函数_桦秋静的博客-CSDN博客

assert(expression) 使用方法

#include<stdio.h>
#include<assert.h>

int main()
{
	int a;
	int b;
	scanf("%d%d",&a,&b);
	float c = (float)a/b;
	assert(b!=0); //当b=0的时候 表示断言失败 其会 想设备打印一条信息,
						//而后通过abort()函数终止程序运行
	printf("%2.2f",c);
	
	return 0;
}

assert() 检测的时候 表达式写简单点, 一般是一个assert()检测一个表达式 。如果一个assert()检测多个表达式,就不知道是哪个表达式有问题,无法正确排除错误。

assert(expression1);
assert(expression2);

assert() 不用会改变环境的语句 作为断言的 表达式

#include<stdio.h>
#include<assert.h>

int main()
{
	int a;
	int i;
	while(i<=100)
	{
		++i;
		assert(++i<100);  //当i 到100的时候终止运行了
		printf("aaaa%d\n",i);
		
	}
	return 0;
}

在 Debug 模式下运行,程序循环到第 101 次时,i 的值为 100,++i <= 100不再成立,断言失败,程序终止运行。

    而在 Release 模式下运行,编译参数中设置了 NDEBUG 宏(如果编译器没有默认设置,那么需要你自己来设置),assert() 会失效,++i <= 100这个表达式也不起作用了,while() 无法终止,成为一个死循环。

    定义了 NDEBUG 宏后,assert(++i <= 100)会被替换为((void)0)。

    导致程序在 Debug 模式和 Release 模式下的运行结果大相径庭的罪魁祸首就是++运算,我们本来希望它改变 i 的值,逐渐达到 while 的终止条件,但是在 Release 模式下它失效了。

———————————————— 版权声明:本文为CSDN博主「桦秋静」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:assert断言函数简介_assert函数_桦秋静的博客-CSDN博客

修改后的代码

把 ,++i 移出来

#include<stdio.h>
#include<assert.h>

int main()
{
	int a;
	int i;
	while(i<=100)
	{
		++i;
		assert(100);  //当i 到100的时候终止运行了
		printf("aaaa%d\n",i);
		
	}
	
	return 0;
}

字符串拼接 strcat() 实现

字符拼接 strcat( str, ber ) 可以将 ber 的字符串 拼接到 str 的尾部

1,调用方式

 #include<stdio.h>
#include<string.h>

int main()
{
	char str[120] = "我们shi_ikun";
	char*p = "苍天已死,黄天当立";
	
	strcat(str,p);
	puts(str);
	return 0;
}

效果

2,自己写strcat()拼接函数

char* Mystrcat(char* dst,const char* src)
{
	char* der = dst;
	while(*dst)
		dst++;
	while((*dst++=*src++)!='\0');
		return der;
}

char* Mystrcat1(char* dst,const char* src)
{
	assert(dst !=NULL ); //断言 如果=NULL就报错
	assert(src !=NULL);
	char* der = dst;
	while(*dst!='\0')
		*dst++;
	while(*src!='\0')
		*dst++=*src++;
	return der;
}

#include<stdio.h>
#include<assert.h>
char* Mystrcat(char* dst,const char* src)
{
	char* der = dst;
	while(*dst)
		dst++;
	while((*dst++=*src++)!='\0');
		return der;
}
char* Mystrcat1(char* dst,const char* src)
{
	assert(dst !=NULL ); //断言 如果=NULL就报错
	assert(src !=NULL);
	char* der = dst;
	while(*dst!='\0')
		*dst++;
	while(*src!='\0')
		*dst++=*src++;
	return der;
}

char* strcopy(char* dst,char* src)
{
	assert(dst !=NULL ); //断言 如果=NULL就报错
	assert(src !=NULL);
	char*der = dst;
	while((*dst++ = *src++)!='\0');
	*dst ='\0';
	return der;
}

int main()
{
	char c[123] = "苍天已死,黄天当立" ;
	char* p = " 岁在甲子,天下大吉";
	char*a =Mystrcat1(c,p);
	puts(a);
	return 0;
}

字符比较strcmp()的使用与实现

int strcmp(const char* s1,const char* s2);
    //若s1 = s2 返回0 s1<s2 返回负数 ;s1>s2 返回正数;
	// 比的是 ASII码值
    
    
 int strncmp(const char* s1, const char* s2, size_t n);
	//功能是将s1与s2进行比较,最多比较 前n个字节
	//若s1=s2 (字符串)返回0;
	//若S1>S2 (字符串) 返回 >0的数;
	//若s1<s2 (字符串)返回<0 的数
    
    

调用 strcmp()

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
	char* a1 = "iz";
	char* a2 = "ik";
	int ret = strcmp(a1,a2);
	printf("ret = %d",ret);
	return 0;
	
}

自己手写 strcmp()比较函数

int cdeclstrcmp (const char * src, const char * dst)
{
    int ret = 0 ;
    while(  !(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
                ++src, ++dst;  
    if ( ret < 0 )
           ret = -1 ;
    else if ( ret > 0 )
           ret = 1 ;
    return( ret );
}

我们要看的是while循环这个语句, ! (ret = (unsigned char )src - (unsigned char )dst)意思是拿指针变量src所指向的字符值(即src)减去指针变量dst所指向的字符值(即dst),差值赋给ret,再取非运算,最后与dst进行与运算; 拿abc例,第一次因为a=a,则执行++src,++dst;2次自加后,ret值为负,跳出while语句,执行if语句的判断,输出为-1; 这里要注意的是: 1.unsigned char是强制转换类型。 2.若src和dst都为空串,返回值是多少呢?因为空串不是指地址为空,还是有地址的,这样就很明确了。

#include<stdio.h>

//字符比较函数
int Mystrcmp(const char* s1,const char* s2)
{
	int der = 0;
	int str1 = 0;
	int str2 = 0;
	char* backstr1 = s1;
	char* backstr2 = s2;
	while(*s1 && *s2 && (*s1==*s2))
	{
		s1++;
		s2++;
	}
	if(*s1||*s2)
	{
		s1 = backstr1;
		s2 = backstr2;
		while(*s1)
		{
			str1+=*s1;
			s1++;
		}
			while(*s2)
		{
			str2+=*s2;
			s2++;
		}
		
	}
	der = backstr1 - backstr2;
	if(der<0)
	{
		der = -1;
	}
	if(der >0)
	{
		der =1;
		
	}
	return der;
	
}

int cdeclstrcmp (const char * src, const char * dst)
{
    int ret = 0 ;
    while(  !(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
                ++src, ++dst;  
    if ( ret < 0 )
           ret = -1 ;
    else if ( ret > 0 )
           ret = 1 ;
    return( ret );
}

int main()
{
	char* a = "ikun";
	char* b = "icao";
	int a1;
	int a2;
	a2 = cdeclstrcmp(a,b);
	a1 = Mystrcmp(a,b);
	printf("%d",a2);
	
}

7,结构体 struct

1,结构体的定义 struct

结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。

通俗的来说其实就是,开辟一个新的变量储这个变量的不同属性,比如,学生成绩,它有语数外三科成绩,因为这三科成绩属 于同一个人。**

结构体可以说是 C语言面向对象的工具

#include<stdio.h>
#include<string.h>
​
struct Student
{
    int num;
    int age;
    char sex;
    char name[32];
    double score;
    char addr[32];
};
​
int main()
{
    struct Student stu1;
    stu1.num = 1;
    stu1.age = 14;
    stu1.sex = 'w';
    strcpy(stu1.name,"蔡徐坤");
    stu1.score = 98.3;
    strcpy(stu1.addr,"贵阳");
    
    printf("学号:%d\n",stu1.num);
    printf("年龄:%d\n",stu1.age);
    printf("性别:%c\n",stu1.sex);
    printf("名字:%s\n",stu1.name);
    printf("分数:%2.2lf\n",stu1.score);
    printf("地址:%s\n",stu1.addr);
    return 0;
}
​
​
​
​
​

2,结构体和数组

结构体数组 是指 一个数组里面 的 每一个元素 都是一个结构体。每一个元素都 包含了 这个结构体里面所定义的所有属性。

访问结构体其内部定义的属性时有两种访问方式,一种是通过变量名,一种是通过结构体指针

变量名 struct Student s.num。通过 . 来访问 结构体内部属性

结构体指针 p->num 通过 -> 来访问 结构体内部的属性

结构体 里面 存放的数据 的地址是连续的,结构体里存放的每一个 数据 地址都是独立的

结构体 所占内存空间大小 = 结构体里面所有 数据 字节之和。

    printf("age: %d  name: %s\n",kun.age,kun.name);//以变量名直接访问结构体里面不同类型的变量。用"."
    printf("age: %d  name: %s",ps->age,ps->name);//通过结构体指针直接访问结构体内部数据。用"->"
    

结构体数组的运用

#include<stdio.h>
​
struct Student
{
    int num;
    char name[32];
    int age;
    char sex;
    double score;
    char addr[32];
};
​
int main()
{
    int arr[3] = {1,3,2};
    int len ;
    int i;
    struct Student arr2[3] = {
        {1,"蔡徐坤",18,'w',98.5,"贵阳"},
        {2,"李素",21,'m',92.2,"六盘水"},
        {3,"徐坤",73,'m',100,"北京"}
    };
    len = sizeof(arr2)/sizeof(arr2[0]);
    for(i = 0;i<len;i++)
    {
        printf("学号:%d  姓名:%s  年龄:%d  性别:%c  分数:%-2.2lf  地址:%s\n",
            arr2[i].num,arr2[i].name,arr2[i].age,arr2[i].sex,arr2[i].score,arr2[i].addr);
    }
    return 0;
}

选票系统

此过程分为 初始化候选人,唱票环节, 打印选票数据,输出最终的选票结果。

#include<stdio.h>
#include<string.h>
struct Xuanmin
{
    char name[32];
    int tickets;
};
​
​
int main()
{
    struct Xuanmin ikun[3];
    struct Xuanmin max;
    int feipiao;
    int i,j;
    int len ;
    int ticketer = 5;
    char tmpName[32];
    int mak;
    
    len = sizeof(ikun)/sizeof(ikun[0]);
    //初始化,环节
    for(i = 0;i<len;i++){
        ikun[i].tickets = 0;
        printf("请输入第%d候选人的名字\n",i+1);
        scanf("%s",&ikun[i].name);
    }
    //唱跳环节
    for(i = 0;i<5;i++)
    {
        mak = 0;
        printf("请输入您投票人的名字\n");
        memset(tmpName,'\0',sizeof(tmpName));
        scanf("%s",&tmpName);
        
        for(j = 0;j<len;j++)
        {
            if(strcmp(tmpName,ikun[j].name)==0)
            {
                ikun[j].tickets++;
                mak = 1;
            }
        }
        if(mak ==0){
            printf("没有此候选人 \n");
            feipiao++;
        }
    }
    
    for(i = 0;i<len;i++)
    {
        printf("名字:%s  票数:%d\n",ikun[i].name,ikun[i].tickets);
    }
    max = ikun[0];
    for(i=0;i<len;i++){
        if(max.tickets<ikun[i].tickets)
        {
            max = ikun[i];
        }
    }
    
        printf("%s以%d票当选,废票有%d\n",max.name,max.tickets,feipiao);
    
    
    return 0;
}

用typedef 关键字 和结构体运用

#include<stdio.h>
#include<string.h>
typedef struct 
{
    int ticker;
    char name[32];
}Expectant;
​

案例

#include<stdio.h>
#include<string.h>
typedef struct 
{
    int ticker;
    char name[32];
}Expectant;
​
int main()
{
    Expectant arr[3];
    Expectant *parr;
    Expectant max;
    int i,j;
    int take = 5;
    char tmpName;
    int make;
    int feipiao = 0;
    
    int len = sizeof(arr)/sizeof(arr[0]);
    
    for(i = 0;i<len;i++)
    {
        printf("请输入第%d个候选人姓名: ",i+1);
        arr[i].ticker = 0;
        scanf("%s",&arr[i].name);
    }   
    for(i = 0;i<take;i++)
    {
        make = 0;
        printf("请输入您选举候选人的名字\n");
        memset(&tmpName,'\0',sizeof(tmpName));
        scanf("%s",&tmpName);
        for(j = 0;j<take;j++){
            if(strcmp(&tmpName,arr[j].name)==0)
            {
                arr[j].ticker++;
                make = 1;
            }
        }
        if(make == 0)
        {
            printf("没有此候选人\n");
            feipiao++;
        }
        getchar();
    }
        
        
    
    for(i=0;i<len;i++)
    {
        printf("候选人姓名:%s  票数:%d 废票有:%d\n ",arr[i].name,arr[i].ticker,feipiao);
    }
    max = arr[0];
    for(i = 0;i<len;i++)
    {
        if(max.ticker<arr[i].ticker)
        {
            max = arr[i];
        }
    }
    printf("恭喜 %s以 %d 票当选省委书记!!!!!!!!\n",max.name,max.ticker);
    
    
    return 0;
}
​

1,结构体指针

1,用变量访问 结构体里面的 属性 就要用 ‘.’

2,用指针间接访问 结构体里面的属性 用‘->’

#include<stdio.h>
#include<string.h>
typedef struct 
{
    int ticker;
    char name[32];
}Expactant;
​
int main()
{
    Expactant arr[3];
    Expactant *parr;
    Expactant max;
    parr = arr;
    //Expactant max;
    int i,j;
    int len ;
    char tempname[32];
    int take=5;
    int make = 0;
    
    len = sizeof(arr)/sizeof(arr[0]);
    //初始化arr
    for(i=0;i<len;i++)
    {
        parr->ticker =0;
        printf("第%d号候选人姓名:\n",i+1);
        scanf("%s",parr->name);
        parr++; 
    }
    //唱票环节
    parr = arr;
    make = 0;
    for(i=0;i<take;i++)
    {
        printf("请输入您选择的候选人\n");
        memset(tempname,'\0',sizeof(tempname));
        scanf("%s",&tempname);
        
        parr = arr;
        for(j = 0;j<len;j++){
            if(strcmp(tempname,parr->name)==0)
            {
                parr->ticker++;
                make = 1;
            }parr++;
        }
        if(make == 0)
        {
            printf("没有候选人\n");
        }
            
    }
    
    parr=arr;
    for(i=0;i<len;i++)
    {
        printf("候选人名字:%s  候选人票数%d\n",parr->name,parr->ticker);
        parr++;
    }
    //指针++每次都会遍历到数组尾巴,下次遍历要记得从新 把指针指向数组首地址
    max = arr[0];
    parr = arr;
    for(i = 0;i<len;i++)
    {
        if(max.ticker<parr->ticker)
            max.ticker = arr[i].ticker;
        parr++;
    }
        printf("恭喜%s 以%d 的票数当选省委书记!!!!!!!\n",max.name,max.ticker);
    return 0;
}

2,结构体指针,数组,函数结合

所需要的头文件

以及结构体

#include<stdio.h>
#include<string.h>
#include <stdlib.h>
typedef struct 
{
    int ticker;
    char name[32];
}Expactant;

结构体指针 函数 初始化 候选人

Expactant *initXm(Expactant*p,int *pn)
{
    int i;
    int tmpename;
    if(p == NULL){
        printf("请输入参与竞选的人数\n");
        scanf("%d",pn);
        p = (Expactant*)malloc(*pn*sizeof(Expactant));
    }
    for(i = 0;i<*pn;i++)
    {
        p->ticker = 0;
        printf("请输入第%d个选民的名字:\n",i+1);
        scanf("%s",p->name);
        p++;
    }
    return p-*pn;
}

唱票环节

​
int getWot(Expactant *p,int len )
{
    Expactant *pbak = p;
    int i,j;
    int feip = 0;
    char tempname[32];
    //int take=5;
    int make = 0;
    make = 0;
    p->ticker = 0;
    for(i=0;i<len;i++)
    {
        printf("请输入您选择的候选人\n");
        memset(tempname,'\0',sizeof(tempname));
        scanf("%s",&tempname);
        
        p = pbak; //把pbak给到p让p回到 首地址
        for(j = 0;j<len;j++){
            if(strcmp(tempname,p->name)==0)
            {
                p->ticker++;
                make = 1;
            }
            p++;
        }
        if(make == 0)
        {
            printf("没有候选人\n");
            feip++;
        }
            
    }
    return feip;
}

输出函数

void printXm(Expactant*p,int pn)
{
    int i;
    for(i=0;i<pn;i++)
    {
        printf("名字:%s,票数:%d\n",p->name,p->ticker);
        p++;
    }
}
​

主函数

int main()
{
    int feip;
    Expactant *XM = NULL;
    int len = 0;
    XM = initXm(XM,&len);
    printXm(XM,len);
    
    //getWot(XM,len );
    feip = getWot(XM,len);
    printf("废票有%d\n",feip);
    printXm(XM,len);
    return 0;
}

4,联合体/共用体基本概念

**联合体 union  {}**  联合体就是共用体,联合体是多种数据类型 共同使用一个空间,也就是说,共用体里面的数据类型不管有多少种,多少个,他们的**地址**是一样的。共用体最大地址是由 共用体里面 最大的数据类型决定的。共用体里面  哪个数据类型所占的字节最大 就确定 这个共用体占了多少个字节。例如:如果最大数据类型是 double  一个double 八个字节 就说明这个共用体就有八个字节的内存空间。

共用体的基本用法

union St{
    int num;
    int age;
    char[32] name;
};
​
int main()
{
    union St s1;
    s1.num = 1; //为共用体 num属性赋值
    printf("%d ",s1.num);
    return 0;
}

#include<stdio.h>
​
struct TestT
{
    int idata;
    char cdata;
    double ddata;
};
​
union TestU
{
    int idata;
    char cdata;
    double ddata;
    
};
​
​
​
int main()
{
    struct TestT t1 = {1,'a',9.2};
    union TestU u1;
    u1.idata = 2;
    u1.cdata = 'c';
    u1.ddata = 89.2;
    //union TestU u2;
    //u2.cdata = 'd';
    //union TestU u3;
    //u3.ddata = 2.3;
    
     
    
    printf("t1 idata = %d\n",t1.idata);
    printf("t1 cdata = %c\n",t1.cdata);
    printf("t1 cdata = %2.2lf\n",t1.ddata);
    printf("t2 cdata = %d\n",u1.idata);
    printf("t2 cdata = %p\n",&u1.idata);
    printf("t2 cdata = %c\n",u1.cdata);
    printf("t2 cdata = %p\n",&u1.cdata);
    printf("t2 cdata = %2.2lf\n",u1.ddata);
    printf("t2 cdata = %p\n",&u1.ddata);
    
    
    
    return 0;
}

2,联合体的作用

联合体(union)是C语言中的一种特殊数据类型,它允许在同一内存位置存储不同类型的成员变量联合体的大小等于其最大成员的大小。

联合体的主要作用是节省内存空间。通过使用联合体,可以在相同的内存位置上存储不同类型的数据,从而避免为每种类型分配独立的内存空间。

除了节省内存外,联合体还可以用于以下几个方面:

  1. 存储不同类型的数据:联合体允许在不同时间存储不同类型的数据。你可以根据需要在联合体中使用一个成员,而其他成员则被忽略。

  2. 数据类型的转换:联合体可以用于进行类型转换。通过在不同类型的成员之间进行赋值,可以将数据从一种类型转换为另一种类型。

  3. 给成员起别名:联合体可以为不同的成员变量提供相同的内存位置,这样可以为这些成员变量起不同的别名,方便访问。

需要注意的是,联合体在同时存储不同类型的数据时,只能使用一个成员来访问数据,而其他成员的值会被覆盖。因此,在使用联合体时要确保正确地访问和操作成员变量,以避免出现意外的结果。

共用体的应用

#include<stdio.h>
​
struct St
{
    char name[32] ;
    int age;
    char sex;
    char zhiye;
    union {
        int class;
        char kemu[32];
    }mes;
    
};
​
int main()
{
    int i;
    int len;
    struct St arr[3];
    len = sizeof(arr)/sizeof(arr[0]);
    
     fleg: 
    for(i=0;i<3;i++)
    {
        printf("请输入您的职业。\n[s]:学生,[t]:老师\n");
        
        scanf("%c",&(arr[i].zhiye));
        //if(arr[i].zhiye !='s'||arr[i].zhiye!='t'){
            //goto fleg;
            //printf("输入错误,请从新输入\n");
    //  }
        if(arr[i].zhiye =='s')
        {
            printf("请输入班级\n");
            scanf("%d",&(arr[i].mes.class));
            printf("请输入您的名字\n");
            scanf("%s",&(arr[i].name));
        }
        if(arr[i].zhiye == 't')
        {
            printf("请输入您授课的科目\n");
            scanf("%s",&(arr[i].mes.kemu));
            printf("请输入您的名字\n");
            scanf("%s",&(arr[i].name));
        }
        getchar();
        if(arr[i].zhiye!='s' && arr[i].zhiye!= 't')
        {
            printf("输入错误,请从新输入\n");
            goto fleg;
        }
        //getchar();
    }
    for(i = 0;i<3;i++)
    {
        if(arr[i].zhiye =='s')
            printf("学生姓名:%s   班级;%d\n",arr[i].name,arr[i].mes.class);
        if(arr[i].zhiye == 't')
            printf("教师姓名:%s   科目:%s\n",arr[i].name,arr[i].mes.kemu);
        
    }
    
    
    return 0;
}

3,枚举类型enum

如果一个变量只有几种可能的值,比如星期几 son mon tus wed thu fri sat

在C语言中,枚举类型(enumeration)用于定义一组具有命名值的常量。枚举类型允许用一个易记的名字来表示一组相关的常量,增强了代码的可读性和可维护性。

枚举类型的定义格式如下:

enum 枚举类型名 {
    枚举常量1,
    枚举常量2,
    ...
};

其中,枚举类型名 是你为枚举类型起的名字,可以根据需要自定义。

在枚举类型的定义中,你可以列出多个枚举常量,每个枚举常量用逗号分隔。枚举常量通常是大写字母命名,以表示它们是常量。

以下是一个示例,展示如何定义一个表示颜色的枚举类型:

#include <stdio.h>
​
enum Color {
    RED,
    GREEN,
    BLUE
};
​
int main() {
    enum Color color1;
    enum Color color2;
​
    color1 = RED;
    color2 = BLUE;
​
    printf("Color1: %d\n", color1);
    printf("Color2: %d\n", color2);
​
    return 0;
}

在上述示例中,我们定义了一个名为 Color 的枚举类型,包含了三个枚举常量 REDGREENBLUE。然后,在 main 函数中声明了两个枚举类型的变量,并将其中的一个变量赋值为 RED,另一个为 BLUE。最后,通过 %d 格式说明符将枚举常量打印为整数值。

输出结果为:

Color1: 0
Color2: 2

每个枚举常量都会被赋予一个默认的整数值,从0开始递增。在上面的示例中,RED 被赋值为0,GREEN 没有直接赋值,默认为1,BLUE 被赋值为2。

你也可以在定义枚举时为每个枚举常量指定特定的值,如:

enum Month {
    JAN = 1,
    FEB = 2,
    MAR = 3,
    ...
};

这样,JAN 的值为1,FEB 的值为2,以此类推。

定义一个 enum类型

enum WeekDay {xinqi1,xinqi2,xinqi3,xinqi4,xinqi5,xinqi6,xinqi7};
//  默认情况下第一个元素是0

可以忽略枚举类型名 直接定义枚举变量,枚举类型里面的元素 名字不能重复

enum {w,e,q,r,s}w1,w2;

4,typedef关键字介绍

给已有的变量取名字,配合 结构体 使用 可以不用写struct 开头

1,typedef 定义作用

在C语言中,typedef 关键字用于为已有的数据类型起一个新的名字。它提供了一种简化和增强代码可读性的方式,使数据类型的定义更加清晰和易于理解。

typedef 的定义格式如下:

typedef 旧类型名 新类型名;

通过使用 typedef,你可以为已有的数据类型起一个新的别名。这个别名就可以像原始类型一样使用,但是它的名称更具有描述性,更方便理解。

以下是一些 typedef 的使用示例:

#include <stdio.h>
​
typedef int 整数;  // 为 int 类型定义别名为 整数
typedef float 小数;  // 为 float 类型定义别名为 小数
​
typedef struct {
    int 年;
    int 月;
    int 日;
} 日期;  // 为结构体类型定义别名为 日期
​
int main() {
    整数 a = 10;
    小数 b = 3.14;
​
    日期 d = {2023, 8, 2};
​
    printf("a: %d\n", a);
    printf("b: %f\n", b);
    printf("日期: %d-%d-%d\n", d.年, d.月, d.日);
​
    return 0;
}

在上述示例中,我们使用 typedef 分别为 int 类型定义了别名 整数,为 float 类型定义了别名 小数,以及为结构体类型定义了别名 日期。这样,我们可以直接使用这些别名来声明变量,并使用它们的更具描述性的名称。

typedef 还可以对指针类型进行别名定义,例如:

typedef int* 整数指针;

这样,整数指针 就成为 int* 类型的别名。

使用 typedef 可以提高代码的可读性,使数据类型的含义更加明确,降低阅读和理解代码的难度。

2,案例

#include<stdio.h>
​
typedef struct 
{
    int num;
    char name[32];
    int age;
    
}person,*pperson; //*pperson 是指针变量
​
void print(person p)
{
    printf("号数: %d  姓名:%s  年龄:%d\n",p.num,p.name,p.age);
    //printf("号数: %d  姓名:%s  年龄:%d\n",p.num,p.name,p.age);
    
}
void print1(pperson p)  //这里p是指针变量 pperson 是 我们人为定义的 结构体指针变量类型 
{
    printf("号数: %d  姓名:%s  年龄:%d\n",p->num,p->name,p->age);
}
​
int main()
{
    person p1 = {1,"简佳人儿",23};
    person p2 = {2,"肥嘟嘟",19};
    
    pperson pp1 = &p1;
    pperson pp2 = &p2;
    
    print(p1);
    print(p2);
    print1(pp1);
    print1(pp2);
    
    
    
    return 0;
}

结构体类型之间不能直接进行比较操作。因此,在比较两个结构体类型的对象时,编译器会报错。

如果你需要比较两个结构体对象的某个字段的值,则可以使用该字段的具体值进行比较。比如,假设 struct Xuanmin 中有一个字段 score,你希望比较两个结构体对象的 score 字段的值大小,可以通过 ikun[i].scoremax.score 来获取这两个对象score 值,然后进行比较。

#include <stdio.h>
​
struct Xuanmin {
    int score;
};
​
int main() {
    struct Xuanmin ikun[3] = { {80}, {90}, {70} };
    struct Xuanmin max = {0};
    int i;
​
    for (i = 0; i < 3; i++) {
        if (ikun[i].score > max.score) {
            max = ikun[i];
        }
    }
​
    printf("Max score: %d\n", max.score);
​
    return 0;
}

在上述示例中,我们通过比较 ikun[i].scoremax.score 的值来找到最高分数的结构体对象。

a3.6_switch_case.c: In function 'main': a3.6_switch_case.c:28:2: error: too few arguments to function 'gets' gets(); ^~~~ In file included from a3.6_switch_case.c:1: C:/MinGW64/x86_64-w64-mingw32/include/stdio.h:658:17: note: declared here char *cdecl gets(char *_Buffer) MINGW_ATTRIB_DEPRECATED_SEC_WARN;

根据提供的错误信息,可以看出在你的代码文件 “a3.6_switch_case.c” 的第28行中,调用了函数 ‘gets’,但是没有传入任何参数。然而,根据C语言的标准库函数,‘gets’ 函数需要至少一个参数来接收输入的字符串。

需要修复这个问题,你可以在调用 ‘gets’ 函数时传入一个字符数组作为参数,用于接收输入的字符串。例如:

char input[100];
gets(input);

此外,需要注意的是,‘gets’ 函数在很多编译器中已经被标记为不安全和不推荐使用的函数,因为它容易导致缓冲区溢出的安全漏洞。建议使用更安全的函数替代,如 ‘fgets’ 函数。使用 ‘fgets’ 函数的示例代码如下:

char input[100];
fgets(input, sizeof(input), stdin);

使用 ‘fgets’ 函数时,需要指定接收输入的字符数组的大小,并传入 ‘stdin’ 作为最后一个参数,表示从标准输入中读取输入。

希望这些信息对你有所帮助!如果有任何其他问题,请随时提问。

2023-07-25 10:36

F:\AIOT\IOT\a_1_C语言基础\第三章流程控制>gcc a3.7_switch_case_xueshengchengji.c a3.7_switch_case_xueshengchengji.c: In function 'main': a3.7_switch_case_xueshengchengji.c:25:12: error: expected ';' before ':' token case 0:~6:;

根据提供的错误信息,可以看到在你的代码文件 “a3.7_switch_case_xueshengchengji.c” 的第25行中,出现了语法错误。错误提示显示在 ‘case’ 语句块的冒号 “:” 之前缺少了一个分号 “;”。

在 switch 语句中,每个 case 后面的语句块结束之前需要加上分号。请检查你的代码,查找并修复这个错误。

正确示例:

case 0:
case 1:
    // 执行语句块
    break;

错误示例(缺少分号):

case 0:  // 错误:缺少分号
case 1:
    // 执行语句块
    break;

希望这个解答对你有所帮助!如有任何其他问题,请随时提问。

2023-07-25 14:44

t--和--t的区别

“t–” 和 “–t” 都是 C 语言中的操作符,用于对变量进行自减操作。它们的区别在于它们的执行时机和返回值:

  1. “t–”(后置自减操作符):先使用变量 t 的当前值,然后再将 t 的值减 1。

    • 执行时机:t-- 是在当前语句执行完之后再进行自减操作。

    • 返回值:t-- 的返回值是变量 t 自减前的值。

示例代码:

int t = 5;
int result1 = t--;  // result1 的值为 5,然后 t 的值变为 4

  1. “–t”(前置自减操作符):先将 t 的值减 1,然后再使用变量 t 的新值。

    • 执行时机:–t 是在当前语句执行之前就进行自减操作。

    • 返回值:–t 的返回值是变量 t 自减后的值。

示例代码:

int t = 5;
int result2 = --t;  // t 的值先减为 4,然后 result2 的值为 4

总结:

  • 如果你希望先使用变量的值再进行自减操作,可以使用后置自减操作符 “t–”。

  • 如果你希望先进行自减操作再使用变量的值,可以使用前置自减操作符 “–t”。

需要注意的是,无论是 “t–” 还是 “–t” 都会改变变量 t 的值。因此,在使用时请根据具体需求选择合适的自减操作符。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-07-25 15:35

a3.13_for1.c: In function 'main': a3.13_for1.c:15:8: warning: passing argument 1 of 'puts' makes pointer from integer without a cast [-Wint-conversion] puts('a'); ^~~ In file included from a3.13_for1.c:1: C:/MinGW64/x86_64-w64-mingw32/include/stdio.h:672:32: note: expected 'const char *' but argument is of type 'int' int _cdecl puts(const char *Str);

根据提供的错误信息,可以看到在你的代码文件 “a3.13_for1.c” 的第15行中,调用了函数 ‘puts’,但是传入的参数是一个字符常量 ‘a’,而不是一个字符串。

‘puts’ 函数的参数类型应该是一个指向字符的指针,即字符串。而单引号括起来的 ‘a’ 表示一个字符,而不是字符串。

要修复这个问题,你需要将参数传递为字符串,即将参数用双引号括起来。示例代码如下:

puts("a");

这样就将字符常量 ‘a’ 转换为了字符串 “a”,然后作为参数传递给了 ‘puts’ 函数。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-07-25 16:26

最大公约数

求两个数的最大公约数有多种方法,其中常见的有辗转相除法和欧几里得算法(辗转相减法)两种。

  1. 辗转相除法(也称为欧几里得算法):

    • 基本思想:假设两个数为 a 和 b,通过反复用较小数去除较大数取余,直到余数为 0,则最后一个非零余数即为最大公约数。

    • 具体步骤:

      • 将 a 除以 b,得到余数 r1。

      • 将 b 除以 r1,得到余数 r2。

      • 后续继续将 r1 除以 r2,如此反复,直到余数 ri 为 0。

      • 最后一个非零余数 ri-1 即为最大公约数。

    • 示例代码如下:

    int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

  2. 辗转相减法:

    • 基本思想:假设两个数为 a 和 b,通过反复用较大数减去较小数,直到两个数相等或其中一个数为 0,则最后一个非零数即为最大公约数。

    • 具体步骤:

      • 如果 a 等于 b,则 a 为最大公约数。

      • 如果 a 大于 b,则 a 减去 b,得到差值 c,再次使用 c 和 b 进行比较。

      • 如果 b 大于 a,则 b 减去 a,得到差值 c,再次使用 c 和 a 进行比较。

      • 如此反复,直到两个数相等或其中一个数为 0。

      • 最后一个非零数即为最大公约数。

    • 示例代码如下:

    int gcd(int a, int b) {
        while (a != b) {
            if (a > b) {
                a -= b;
            } else {
                b -= a;
            }
        }
        return a;
    }

可以根据需要选择其中一种方法来求解最大公约数。这些示例代码中的函数 gcd(a, b) 接受两个整数参数 a 和 b,并返回它们的最大公约数。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-07-25 17:21

size_t

size_t 是C语言中的一个数据类型,它用于表示内存大小或对象的大小。它是无符号整数类型,通常是一个无符号整数的别名。

在C标准库中,很多函数的参数或返回值的类型使用了 size_t,例如 strlen 函数的返回值类型就是 size_t

size_t 的大小在不同的平台上可能会有所不同,但通常它是足够大以容纳任何对象的大小。它是一种机器相关的类型,在 32位系统上一般是 32位,在 64位系统上一般是 64位。

使用 size_t 类型的一个常见用法是在内存分配函数(如 mallocrealloccalloc)中指定分配的字节数。此外,它还可以用于循环计数器以及表示数组的索引等场景。

下面是一个使用 size_t 的示例:

#include <stdio.h>
​
int main() {
    size_t n = 10;
    size_t i;
​
    for (i = 0; i < n; i++) {
        printf("%zu\n", i);
    }
​
    return 0;
}

在打印循环计数器的时候,使用 %zu 格式说明符来输出 size_t 类型的值。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-01 11:08

size_t源码

size_t 是 C 语言的标准库类型,通常由编译器提供。它的具体实现可以在编译器的头文件中找到。

size_t 的定义通常位于 <stddef.h> 头文件中,该头文件中还定义了其他与大小相关的类型和宏。

以下是一个 stddef.h 头文件中可能包含的 size_t 的简化示例定义:

#ifndef _SIZE_T
#define _SIZE_T
​
typedef /*implementation-defined*/ size_t;
​
#endif /* _SIZE_T */

具体的实现细节可以因编译器而异。在不同的平台和编译器上,size_t 的具体定义可能存在差异。在上述示例中,size_t 被定义为一个 implementation-defined 的类型。具体来说,它可能是 unsigned intunsigned longunsigned long long 等。

使用特定的编译器来编译代码时,可以查看其相应的标准库头文件,以了解 size_t 的实际定义。例如,在使用 GCC 编译器的情况下,可以查看 /usr/include/stddef.h 文件。

需要注意的是,虽然可以查看 size_t 的实现,但为了编写可移植的代码,应该始终使用标准库头文件 stddef.h 中定义的 size_t,而不是直接依赖于具体实现。

希望这能回答你的问题!如果还有其他问题,请随时提问。

2023-08-01 11:09

0取反得到什么

在计算机中,对整数进行取反操作是一种按位操作,通常使用位求反(bitwise NOT)运算符进行。

当一个整数进行位求反运算时,将会对其所有位进行取反操作,即将所有二进制位的0变为1,将所有二进制位的1变为0。

例如,对于二进制数 0000 0000,也就是十进制数 0,进行位求反运算后,结果为 1111 1111,也就是十进制数 -1。

需要注意的是,位求反运算会根据数据类型的大小仅对有效的位进行操作。例如,对于32位整数,位求反运算只会对前32位进行取反,如果有更高位的话,不会受到影响。

请记住,对于有符号整数,取反操作将会得到其按位取反后的结果,而不是取负数的操作。如果要得到一个有符号整数的相反数,应使用负号(取负)运算符。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-01 19:17

a_8.1_jiegoutichushi.c: In function 'main': a_8.1_jiegoutichushi.c:19:12: error: assignment to expression with array type stu1.name = "ikun";

根据你提供的错误信息,请注意在C语言中,数组是一种特殊的数据类型,无法直接使用赋值操作符将一个字符串赋值给一个数组。出现错误的原因是你尝试将一个字符串赋值给 stu1.name 这个数组。

如果你想给一个数组赋值,可以使用字符串拷贝函数 strcpy 或字符串赋值函数 strncpy 来完成。这两个函数可以将一个字符串复制到数组中。

示例代码如下:

#include <stdio.h>
#include <string.h>
​
struct Student {
    char name[20];
    int age;
};
​
int main() {
    struct Student stu1;
​
    // 使用 strcpy 函数将字符串赋值给数组
    strcpy(stu1.name, "ikun");
    stu1.age = 18;
​
    printf("Name: %s\n", stu1.name);
    printf("Age: %d\n", stu1.age);
​
    return 0;
}

在上述示例中,我们使用 strcpy 函数将字符串 “ikun” 复制到 stu1.name 数组中。这样就可以给数组赋值了。

当然,要确保目标数组有足够的空间来容纳要复制的字符串,以免发生内存溢出的情况。在使用字符串拷贝函数时应当特别注意。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-01 19:59

a_8.3_struct_array_xuanpiao.c: In function 'main': a_8.3_struct_array_xuanpiao.c:54:9: error: invalid operands to binary < (have 'struct Xuanmin' and 'struct Xuanmin') if(max<ikun[i])

根据你提供的错误信息,请注意在C语言中,结构体类型之间不能直接进行比较操作。因此,在比较两个结构体类型的对象时,编译器会报错。

如果你需要比较两个结构体对象的某个字段的值,则可以使用该字段的具体值进行比较。比如,假设 struct Xuanmin 中有一个字段 score,你希望比较两个结构体对象的 score 字段的值大小,可以通过 ikun[i].scoremax.score 来获取这两个对象的 score 值,然后进行比较。

示例代码如下:

#include <stdio.h>
​
struct Xuanmin {
    int score;
};
​
int main() {
    struct Xuanmin ikun[3] = { {80}, {90}, {70} };
    struct Xuanmin max = {0};
    int i;
​
    for (i = 0; i < 3; i++) {
        if (ikun[i].score > max.score) {
            max = ikun[i];
        }
    }
​
    printf("Max score: %d\n", max.score);
​
    return 0;
}

在上述示例中,我们通过比较 ikun[i].scoremax.score 的值来找到最高分数的结构体对象。

请根据你的实际需求,选择要比较的字段,并进行相应的更改。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 09:51

a_8.4_struct_pointer.c: In function 'main': a_8.4_struct_pointer.c:13:11: error: assignment to expression with array type ps->name ="蔡徐坤";

根据你提供的错误信息,请注意在C语言中,数组是一种特殊的数据类型,无法直接使用赋值操作符将一个字符串赋值给一个数组。出现错误的原因是你尝试将一个字符串赋值给 ps->name 这个数组。

如果你想给一个数组赋值,可以使用字符串拷贝函数 strcpy 或字符串赋值函数 strncpy 来完成。这两个函数可以将一个字符串复制到数组中。

示例代码如下:

#include <stdio.h>
#include <string.h>
​
struct Student {
    char name[20];
    int age;
};
​
int main() {
    struct Student s;
    struct Student *ps;
​
    ps = &s;
​
    strcpy(ps->name, "蔡徐坤");
    ps->age = 18;
​
    printf("Name: %s\n", ps->name);
    printf("Age: %d\n", ps->age);
​
    return 0;
}

在上述示例中,我们使用 strcpy 函数将字符串 “蔡徐坤” 复制到 ps->name 数组中。这样就可以给结构体中的数组字段赋值了。

当然,要确保目标数组有足够的空间来容纳要复制的字符串,以免发生内存溢出的情况。在使用字符串拷贝函数时应当特别注意。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 10:26

a_8.11_struct_union.c:22:21: note: (near initialization for 'u1') a_8.11_struct_union.c:22:25: warning: excess elements in union initializer union TestU u1= {2,'d',9.3};

根据你提供的警告信息,请注意在C语言中,联合(union)是一种特殊的数据类型,它只能存储其中一个成员的值。在初始化联合时,只能为其中一个成员赋值。

发生警告的原因是你在初始化联合时给了多余的元素。在你的代码中,联合 u1 的定义如下:

union TestU u1 = {2, 'd', 9.3};

根据警告信息,联合 u1 包含了超过一个成员的初始化值。联合只能存储一个成员的值,因此,应该只使用一项初始化值。

你可以根据需要选择给联合的一个成员赋值,如下所示:

union TestU {
    int num;
    char character;
    float decimal;
};
​
int main() {
    union TestU u1 = {2}; // 给联合的整型成员赋值
​
    // 其他代码...
​
    return 0;
}

在上面的示例中,我们给联合 u1 的整型成员 num 赋值为2。这样,联合 u1 就只有一个成员的初始化值,不会出现警告。

需要注意的是,在使用联合时需谨慎,确保正确地访问和操作联合的成员。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 16:24

联合体的作用

联合体(union)是C语言中的一种特殊数据类型,它允许在同一内存位置存储不同类型的成员变量。联合体的大小等于其最大成员的大小。

联合体的主要作用是节省内存空间。通过使用联合体,可以在相同的内存位置上存储不同类型的数据,从而避免为每种类型分配独立的内存空间。

除了节省内存外,联合体还可以用于以下几个方面:

  1. 存储不同类型的数据:联合体允许在不同时间存储不同类型的数据。你可以根据需要在联合体中使用一个成员,而其他成员则被忽略。

  2. 数据类型的转换:联合体可以用于进行类型转换。通过在不同类型的成员之间进行赋值,可以将数据从一种类型转换为另一种类型。

  3. 给成员起别名:联合体可以为不同的成员变量提供相同的内存位置,这样可以为这些成员变量起不同的别名,方便访问。

需要注意的是,联合体在同时存储不同类型的数据时,只能使用一个成员来访问数据,而其他成员的值会被覆盖。因此,在使用联合体时要确保正确地访问和操作成员变量,以避免出现意外的结果。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 19:44

枚举类型的定义

在C语言中,枚举类型(enumeration)用于定义一组具有命名值的常量。枚举类型允许用一个易记的名字来表示一组相关的常量,增强了代码的可读性和可维护性。

枚举类型的定义格式如下:

enum 枚举类型名 {
    枚举常量1,
    枚举常量2,
    ...
};

其中,枚举类型名 是你为枚举类型起的名字,可以根据需要自定义。

在枚举类型的定义中,你可以列出多个枚举常量,每个枚举常量用逗号分隔。枚举常量通常是大写字母命名,以表示它们是常量。

以下是一个示例,展示如何定义一个表示颜色的枚举类型:

#include <stdio.h>
​
enum Color {
    RED,
    GREEN,
    BLUE
};
​
int main() {
    enum Color color1;
    enum Color color2;
​
    color1 = RED;
    color2 = BLUE;
​
    printf("Color1: %d\n", color1);
    printf("Color2: %d\n", color2);
​
    return 0;
}

在上述示例中,我们定义了一个名为 Color 的枚举类型,包含了三个枚举常量 REDGREENBLUE。然后,在 main 函数中声明了两个枚举类型的变量,并将其中的一个变量赋值为 RED,另一个为 BLUE。最后,通过 %d 格式说明符将枚举常量打印为整数值。

输出结果为:

Color1: 0
Color2: 2

每个枚举常量都会被赋予一个默认的整数值,从0开始递增。在上面的示例中,RED 被赋值为0,GREEN 没有直接赋值,默认为1,BLUE 被赋值为2。

你也可以在定义枚举时为每个枚举常量指定特定的值,如:

enum Month {
    JAN = 1,
    FEB = 2,
    MAR = 3,
    ...
};

这样,JAN 的值为1,FEB 的值为2,以此类推。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 20:53

typedef 的定义作用

在C语言中,typedef 关键字用于为已有的数据类型起一个新的名字。它提供了一种简化和增强代码可读性的方式,使数据类型的定义更加清晰和易于理解。

typedef 的定义格式如下:

typedef 旧类型名 新类型名;

通过使用 typedef,你可以为已有的数据类型起一个新的别名。这个别名就可以像原始类型一样使用,但是它的名称更具有描述性,更方便理解。

以下是一些 typedef 的使用示例:

#include <stdio.h>
​
typedef int 整数;  // 为 int 类型定义别名为 整数
typedef float 小数;  // 为 float 类型定义别名为 小数
​
typedef struct {
    int 年;
    int 月;
    int 日;
} 日期;  // 为结构体类型定义别名为 日期
​
int main() {
    整数 a = 10;
    小数 b = 3.14;
​
    日期 d = {2023, 8, 2};
​
    printf("a: %d\n", a);
    printf("b: %f\n", b);
    printf("日期: %d-%d-%d\n", d.年, d.月, d.日);
​
    return 0;
}

在上述示例中,我们使用 typedef 分别为 int 类型定义了别名 整数,为 float 类型定义了别名 小数,以及为结构体类型定义了别名 日期。这样,我们可以直接使用这些别名来声明变量,并使用它们的更具描述性的名称。

typedef 还可以对指针类型进行别名定义,例如:

typedef int* 整数指针;

这样,整数指针 就成为 int* 类型的别名。

使用 typedef 可以提高代码的可读性,使数据类型的含义更加明确,降低阅读和理解代码的难度。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

2023-08-02 21:24

a_8.7_struct_puinter3_hanshu.c: In function 'printArr': a_8.7_struct_puinter3_hanshu.c:58:48: error: subscripted value is neither array nor pointer nor vector printf("候选人姓名: %s 候选人票数:%d",arr[i].name,arr[i].tickets);

根据你提供的错误信息,编译错误发生在函数 printArr 中的 printf 语句。错误提示说,订阅的值既不是数组、指针,也不是向量。

问题在于你使用了 . 运算符来访问结构体数组 arr 中的成员变量。然而,结构体数组应使用索引操作符 [] 来访问特定的元素。

要解决这个错误,你需要将 . 运算符改为 [] 索引操作符,代码如下所示:

printf("候选人姓名: %s   候选人票数:%d", arr[i].name, arr[i].tickets);

通过使用 [] 索引操作符,你可以正确地访问结构体数组中特定索引位置的结构体,并访问其成员变量。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

a_8.6.1_struct_pointer.c: In function 'main': a_8.6.1_struct_pointer.c:13:10: error: assignment to expression with array type s1.name = "坤坤";

根据你提供的错误信息,编译错误发生在 main 函数中的第 13 行。错误提示说,你尝试将一个表达式赋值给一个数组类型的表达式。

问题在于你尝试将字符串常量赋值给结构体 s1name 成员,但是 name 成员是一个数组类型,不能直接进行赋值操作。

要解决这个错误,你可以使用字符串拷贝函数 strcpy 来将字符串常量赋值给结构体 s1name 成员。需要确保结构体 s1name 成员数组足够大以容纳字符串常量。

以下是一个修改后的示例:

#include <stdio.h>
#include <string.h>
​
struct Student {
    char name[20];
    int age;
    float gpa;
};
​
int main() {
    struct Student s1;
​
    strcpy(s1.name, "坤坤");
    s1.age = 20;
    s1.gpa = 3.8;
​
    printf("姓名:%s\n", s1.name);
    printf("年龄:%d\n", s1.age);
    printf("GPA:%f\n", s1.gpa);
​
    return 0;
}

在上述示例中,我们引入了头文件 <string.h>,并使用 strcpy 函数将字符串常量 "坤坤" 复制到结构体 s1name 成员中。

使用字符串拷贝函数 strcpy 可以安全地将字符串复制到结构体数组中,确保不会发生缓冲区溢出等问题。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

a_8.6.2_struct_pointer_app.c: In function 'main': a_8.6.2_struct_pointer_app.c:29:10: warning: passing argument 1 of 'memset' makes pointer from integer without a cast [-Wint-conversion] memset(tmpName,'\0',sizeof(tmpName));

根据你提供的警告信息,警告发生在 main 函数中的第 29 行。警告说,你将整数类型的值作为指针传递给了 memset 函数。

问题在于 tmpName 是一个字符数组,而 memset 函数需要使用指针作为第一个参数,以指定要设置的内存块的起始地址。然而,你传递给 memset 函数的 tmpName 是一个数组名,当作为参数传递时,会隐式地转换为指向数组首元素的指针,所以产生了警告。

要解决这个警告,你可以使用数组的地址(即数组名前加上 & 符号)作为 memset 函数的第一个参数。

以下是一个修改后的示例:

#include <stdio.h>
#include <string.h>
​
struct Student {
    char name[20];
    int age;
    float gpa;
};
​
int main() {
    struct Student s1;
​
    char tmpName[20];
    strcpy(tmpName, "坤坤");
    memset(&tmpName, '\0', sizeof(tmpName));
​
    printf("姓名:%s\n", s1.name);
    printf("年龄:%d\n", s1.age);
    printf("GPA:%f\n", s1.gpa);
​
    return 0;
}

在上述示例中,我们首先定义了一个临时的字符数组 tmpName,并使用 strcpy 函数将字符串常量 "坤坤" 复制到 tmpName 中。然后,我们使用 memset 函数来将 tmpName 数组的内容初始化为全零。注意,在向 memset 函数传递 tmpName 的地址时,我们使用了 & 符号来获取数组的地址。

这样,我们可以避免警告,并正确地使用 memset 函数对数组进行内存初始化。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

a_8.7_struct_pointer5_hanshu.c: In function 'main': a_8.7_struct_pointer5_hanshu.c:78:9: warning: passing argument 1 of 'printf' from incompatible pointer type [-Wincompatible-pointer-types] printf(XM,len);

根据你提供的警告信息,警告发生在 main 函数中的第 78 行。警告说,你将一个不兼容的指针类型作为参数传递给了 printf 函数的第一个参数。

问题在于在 printf 函数的第一个参数中,你传递了 XM,而 XM 是一个指向字符数组 char XM[20] 的指针。然而,printf 函数期望的第一个参数是一个字符串格式的连续字符序列,而不是一个指针。

要解决这个警告,你应该传递一个格式字符串,而不是指针。你可以使用 %s 占位符来表示字符串,并在 printf 函数的第一个参数中提供格式字符串和需要打印的变量。

以下是一个修改后的示例:

#include <stdio.h>
​
void printInfo(char *name, int age) {
    printf("姓名:%s\n", name);
    printf("年龄:%d\n", age);
}
​
int main() {
    char XM[20] = "小明";
    int age = 18;
​
    printInfo(XM, age);
​
    return 0;
}

在上述示例中,我们定义了一个函数 printInfo,接受一个字符指针 name 和一个整数 age 作为参数。在 printInfo 函数内部,我们使用 %s 占位符来打印字符串,并使用 %d 占位符来打印整数。

main 函数中,我们传递了 XM 字符数组和 age 变量作为参数调用了 printInfo 函数。

这样,我们可以避免警告,并正确地打印字符串和整数变量。

希望这个解答对你有所帮助!如果还有其他问题,请随时提问。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱坤学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值