数据类型
int a =10;//整型char b =1;//字符数据类型short c =3;//短整型long d =4;//长整型float e =12.3;//单精度浮点型double f =12.33;//双精度浮点型printf("int大小:%d\n",sizeof(a));//4printf("char大小:%d\n",sizeof(b));//1printf("short大小:%d\n",sizeof(c));//2printf("long大小:%d\n",sizeof(d));//4printf("long long大小:%d\n",sizeof(longlong));//8printf("float大小:%d\n",sizeof(e));//4printf("double大小:%d\n",sizeof(f));//8
类型的意义
- 使用这个类型开辟了多大的空间
- 如何看待内存空间的视角(int能访问4个字节)
整形的范围
整型范围的公式: -2(n-1)
2(n-1)-1 (n为整型的内存占用位数),所以 int 类型 32 位那么就是 -(2^31) ~ 2^31-1 即 -214748364821474836470000 0001
1111 1111
1 0000 0000 …………………… 由于 char 为 8 位,最高位 1 被丢弃结果为 0
环形char
char a[1000];int i;for(i =0; i<1000; i++){
a[i]=-1- i;}printf("%d",strlen(a));//255return0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vtHcyhyg-1674720201666)(C:\Users\75935\Desktop\csdn照片\环形char.png)]
数据的存储
原码,反码,补码
- 原码:直接将二进制按照正负数的形式翻译成二进制就行
- 反码:原码的符号位不变,其他位按位取反
- 补码:反码+1
intmain(){char a =-1;//10000000000000000000000000000001 //在内存中先与整形看待,再截取//11111111111111111111111111111110//11111111111111111111111111111111//11111111 ---a//补码:11111111111111111111111111111111 整形提升补的符号位是0/1看a变量类型有无符号//反码:11111111111111111111111111111110//原码:10000000000000000000000000000001signedchar b =-1;//11111111 ---bunsignedchar c =-1;//11111111 ---c//补码00000000000000000000000011111111 整形提升//反码00000000000000000000000011111111 无符号数原,反,补码相同//原码00000000000000000000000011111111 printf("a=%d,b=%d,c=%d", a, b, c);// %d有符号整形char a =128;/char a=-128//打印出来的值是一样的printf("%u\n",a);//按补码来算int i=-4;unsignedint j =5;printf("%d\n", i+j);//1unsignedint a =-1;//10000000000000000000000000000001//11111111111111111111111111111110//11111111111111111111111111111111 //补码//在内存中的存储2个都是补码一样的,//当以%d打印的时候,就会转为原码再打印//当以%u打印的时候,因为是无符号的a原码和补码一样不用转printf("%d\n", a);//-1printf("%u\n", a);//4294967295unsignedchar a =200;unsignedchar b =100;unsignedchar c =0;
c = a + b;printf(“%d %d”, a+b,c);//44 300//a+b的结果是用一个四字节的整数接收的,不会越界。//而c已经在c = a + b这一步中丢弃了最高位的1,所以只能是300-256得到的44了。//由于printf是可变参数的函数,所以后面参数的类型是未知的,所以甭管你传入的是什么类型,//printf只会根据类型的不同将用两种不同的长度存储。其中8字节的只有long long、float和double//(注意float会处理成double再传入),其他类型都是4字节。所以虽然a + b的类型是char,//实际接收时还是用一个四字节整数接收的。//另外,读取时,%lld、%llx等整型方式和%f、%lf等浮点型方式读8字节,其他读4字节。return0;}
正数的原,反,补码相同,对整形来说数据存放在内存中的是补码
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统
一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程
是相同的,不需要额外的硬件电路
大端,小端
大端(存储)模式:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
小端模式:数据的低位保存在内存的低地址中,数据的高位保存在高地址中
例如在一个16bit的short型的a中,在内存的地址为0x1234,a的值为0x5566,那么0x55为高字节,0x66为低字节
为什么有大小端呢?
在计算机系统中,我们以字节为单位,一个字节等于个8bit,对于大于8位的处理器存在如何安排多个字节的问题
验证机器大小端
intmain(){int a =1;char*p =&a;if(*p =1){printf("小端\n");}elseprintf("大端\n");return0;}
浮点型在内存的存储
浮点数包括:float,double,long double
浮点数表示
(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,大于等于1,小于2。 //保留M时舍弃第一位1
2^E表示指数位 E为一个无符号整数(unsigned int )E为8位,它的取值范围为0255;如果E为11位,它的取值范围为02047
要加个中间数8位加127,11位加1023
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oN4o5rti-1674720201668)(C:\Users\75935\Desktop\csdn照片\浮点数内存存储表.png)]
关于E的取出
- E不为全0或者全1> 8位-127,11位-1023就可以得出真实的值,再将有效数字M前加上第一位的1
- E为全0> 浮点数的指数E等于1-127(或者1-1023)即为真实值,> 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于> 0的很小的数字
- E为全1> 如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
float f =4.5f;//100.1 小数点后面的1代表2^-1 //(-1)^0 *1.001*2*2//S=0//M=1.001//E=2 +127//0 10000001 10010000 00000000 0000000 //40 90 00 00 16进制 4个一组int n =9;//0 00000000 00000000000000000001001//写成浮点数//-1^0*0.00000000000000000001001*2^(-126)=1.001*2^(-146`) float*pFloat =(float*)&n;printf("n的值为:%d\n", n);//n的值为:9printf("*pFloat的值为:%f\n",*pFloat);//*pFloat的值为:0.000000*pFloat =9.0;//1001.0//1.001*2^3//0 10000010 10010000 00000000 0000000printf("num的值为:%d\n", n);//num的值为:1091567616printf("*pFloat的值为:%f\n",*pFloat);//*pFloat的值为:9.000000
变量/常量
作用域:变量可用范围就是该作用域
- 局部变量作用域:变量所在的局部范围
- 全局变量作用域:整个工程
生命周期
变量的生命周期是指创建到销毁的时间段
- 局部变量生命周期:进入作用域开始到出作用域结束
- 全局变量生命周期:整个程序的生命周期
int NUM=1221//全局变量 全局变量,没有给初始值时,编译其会默认将其初始化为0int main
{int NUM =12;//局部变量printf("NUM:%d\n", NUM);//局部变量和全局变量同名时,优先使用局部变量//局部变量的使用int num1 =2;int num2 =3;scanf_s("%d%d",&num1,&num2);int sub = num1 + num2;printf("sub=%d+%d=%d\n",num1,num2, sub);}
常量
- 整形常量:10进制,八进制(017 -0开头的就是8进制),16进制那些数
- 实型常量:浮点型常量float,double,e与E,表示以10为底数的幂数,且e与E后面必须跟整数,若是小数,也是错误的
- 字符型、字符串常量:如 ‘ a’,“ab” 必须加这个符号
- 转义字符常量:’ \0 ’
- define宏定义类型的
#defineg12;//denfine修饰的常量//枚举特点//第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1enumDAY{
MON=1,//枚举常量
TUE,
WED,};int main
{//常量100;//字面常量constint const_a =10;//const修饰常变量,a是不可以被修改的enumStudent day;
day = WED;char*p="advd";//不能修改的printf("%d\n", WED);}
字符串
intmain(){//字符串//字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志char arr1[]="hello";char arr2[]={'h','e','l','l','o'};//errorchar arr3[]={'h','e','l','l','o','\0'};printf("%s\n", arr1);printf("%s\n", arr2);printf("%s\n", arr3);return0;}
转义字符 \
intmain(){//strlen从前往后依次检测,直到遇到'\0'是就终止检测。//转义字符printf("%c\n",'\'');printf("%d\n",strlen("abcdef"));//6// \62被解析成一个转义字符printf("%d\n",strlen("c:\test\628\test.c"));//14printf("%d\n",strlen("c:\test\121"))//7// \t转移字符,水平制表 //是讲121看做8进制数组,转换为10进制后的81,作业为ASCII码值的字符,即:字符'Q' , return0;}
字符串函数
strlen
size_t strlen ( const char * str )
- 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
- 参数指向的字符串必须要以 ‘\0’ 结束。
- 注意函数的返回值为size_t,是无符号的
//模拟实现strlen//1.计数器//int my_strlen(const char*ch)//{// int count = 0;// while (*ch)// {// count++;// ch++;// }// return count;//}//2.指针-指针//int my_strlen(const char *ch)//{// const char *str = ch;// while (*ch)// {// ch++;// }// return ch - str;//}//3.不创建临时变量计数器intmy_strlen(constchar*ch){if(*ch =='\0')return0;elsereturn1+my_strlen(ch +1);}intmain(){char*str1 ="abcdef";int ret=my_strlen(str1);printf("%d\n", ret);}
strcpy
char strcpy(char * destination, const char * source );*
- 源字符串必须以 ‘\0’ 结束。
- 会将源字符串中的 ‘\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变
char *my_strcpy(char* dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while ((*dest++ = *src++));
return ret;
}
//strcpy
int main()
{
char ch[20];
my_strcpy(ch, "hh");
return 0;
}
strncpy
char * strncpy(char * destination, const char * source, size_t num);
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
intmain(){char a[10]="abcd";char b[5]="efg";strncpy(a, b,4);return0;}
strcat
**char *strcat( char strDestination, const char strSource );
自己给自己追加?
这个函数不能实现自己给自己追加的原因在于;两个指针同时操作一个字符串, 当*dest找到 '\0' 之后,*src开始追加,当追加完一个字符后,*src往后走一位, *dest指向的 '\0'被覆盖,*dest就必须往后走一位,找到下一个'\0',找到之后*src又开始追加, 不断重复,导致程序崩溃。
char*my_strcat(char*dest,constchar*src)//字符串追加{assert(dest !=NULL);assert(src !=NULL);char*ret = dest;//1.找尾while(*dest){
dest++;}//2.追加while((*dest++=*src++)){;}return ret;}intmain(){char arr[20]="hello";char*p ="world";char*ret=my_strcat(arr, p);printf("%s\n", ret);return0;}
strncat
char * strncat ( char * destination, const char * source, size_t num );
intmain(){char a[20]="abcdefg";char b[10]="hijk";strncat(a, b,7);return0;}
strcmp
比较的是字符串的内容,对应的ASCLL值
int strcmp ( const char * str1, const char * str2 );
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
intmy_strcmp(constchar* src,constchar* dst){int ret =0;assert(src !=NULL);assert(dest !=NULL);while(!(ret =*(unsignedchar*)src -*(unsignedchar*)dst)&&*dst)++src,++dst;if( ret <0)
ret =-1;elseif( ret >0)
ret =1;return( ret );}
strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
intmain(){char a[][5]={"AB11","ab11","AB22"};for(int i =0; i <3; i++){if(strncmp(a[i],"ABxx",2)==0)printf("%s\n",a[i]);}return0;}
strstr
char * strstr ( const char *str1, const char * str2);
字符串查找,在str1里找str2,找到就返回第一次找到的位置,找不到返回NULL
char*my_strstr(constchar*str1,constchar* str2){assert(str1 && str2);char* s1;char* s2;char* cp = str1;if(*str2 =='\0')return str1;//cp是来记录从这个字母开始比较的,如果这次比较失败就++,下个字母开始重新比较。while(*cp){
s1 = cp;
s2 = str2;//while (*s1!='\0' && *s2 != '\0' && *s1 == *s2)while(*s1 &&*s2 &&*s1 ==*s2){
s1++;
s2++;}if(*s2 =='\0'){return cp;}
cp++;}//找不到returnNULL;}intmain(){char a[50]="is an apple ,oh a apple";char b[10]="applee";char*pret =strstr(a, b);if(pret ==NULL){printf("找不到\n");}elseprintf("找到了!\n");return0;}
strtok
char * strtok ( char * str, const char * sep )
- sep参数是个字符串,定义了用作分隔符的字符集合
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
intmain(){char str1[]="gitee.com/jiantaoWU";char sep[]="./";char tmp[100]={0};char*ret =NULL;strcpy(tmp, str1);//strtok(tmp, sep); //gitee\0 com/jiantaoWU 返回g的地址//strtok(NULL, sep);//记录截端的位置并从这开始c//ret!=NULL说明找到了for(ret =strtok(tmp, sep); ret !=NULL; ret =strtok(NULL, sep)){printf("%s\n", ret);}/*gitee
com
jiantaoWU*/}
strerr
char * strerror ( int errnum ); //返回错误码对应的错误信息
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
内存函数
memcpy
void * memcpy ( void * destination, const void * source, size_t num )
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 ‘\0’ 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的
void*my_memcpy(void* dest,constvoid*str,size_t num){//一个字节一个字节拷贝void*ret = dest;assert(dest&&str);while(num--){*(char*)dest =*(char*)str;
dest =(char*)dest +1;
str =(char*)str +1;}return ret;}intmain(){int arr1[]={1,2,3,4,5,6,7,8};int arr2[10]={0};my_memcpy(arr2, arr1,8*sizeof(int));my_memcpy(arr1+2,arr1,3*sizeof(int));//error 不能重叠拷贝 for(int i =0; i <10; i++){printf("%d ", arr2[i]);}return0;}
memmove
void * memmove ( void * destination, const void * source, size_t num )
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
//memmovevoid*my_memove(void*dest,constvoid*str,size_t num){assert(dest&&str);void*ret = dest;if(dest > str){//后向前拷贝while(num--){*((char*)dest+num)=*((char*)str+num);}}else{//前向后拷贝while(num--){*(char*)dest =*(char*)str;
str =(char*)str+1;
dest =(char*)dest +1;}}return ret;}intmain(){int arr[10]={1,2,3,4,5,6,7,8,9,10};my_memove(arr, arr+2,4*sizeof(int));for(int i =0; i <10; i++){printf("%d ", arr[i]);}return0;}
memcmp
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
比较从ptr1和ptr2指针开始的num个字节
return <0 prt1<ptr2
memset
void *memset(void *dest, int c, size_t count);
//memset
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
memset(arr, 0, 20);//把前20个字节全部设置为0
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
动态内存管理
关键字
typedef
对类型定义别名,可以是结构体
typedefunsignedint uint;typedefstructNumber{int ID;}Num;//注意 使用typedef 指针时最好 typedef const int * p_inttypedefint* p_int;//errorvoid(const p_int a)//这里相当于int *const 相当于指针是const指针,//而不是我们要的const *int intmain(){//typedef 类型重命名unsignedint a =0;
uint d=0;//2个类型的一样的
Num num1;
num1.ID =12;printf("%d\n", num1.ID);}
define
- #define又称宏定义,标识符为所定义的宏名,简称宏,define 的功能是将标识符定义为常量
- 定义的标识符不占内存,只是一个临时的符号
#defineSUB(x,y)((x)-(y))#defineNUM12#defineDSUBSUB(x,y)*2//宏可以套//#define 宏是替换 可以用#undef 终止宏的作用域intmain(){printf("%d\n", NUM);int x =2;int y =3;int result =SUB(x, y);printf("NUM\n");//宏在" " 里不进行替换printf("%d\n", result);printf("%d\n", DSUB);return0;}
#define vs typedef
- 宏定义只是简单的字符串替换,由预处理器来处理;而 typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字,将它作为一种新的数据类型。
#definep_int1int*typedefint* p_int2intmain(){ p_int1 a,b;// int *a ,int b p_int2 c,d;// int *a ,int *b}
static
static 被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间
static修饰函数
- 非静态函数可以在另一个文件直接引用,static函数只能在该文件中使用不能跨文件调用会出现链接错误
- 不同的文件可以使用相同名字的静态函数,互不影响
static修饰局部变量
- 延长了局部变量的生命周期
- 对静态关键字修饰的局部变量的初始化。
- 在编译的过程中,会在数据区为该变量开辟空间,并对其进行初始化,如果代码中未对其进行初始化,则系统默认初始化为0
intfun(int x){static a;//编译时初始化为0static b=x;//等运行fun函数才进行赋值}
static修饰全局变量
- 在全局数据区分配存储空间,且编译器会自动对其初始化。
- 静态全局变量仅对当前文件能使用,其他文件可以用相同的名字
voidtest(){staticint i =0;
i++;printf("%d ", i);}voidfun(){printf("fun()\n");}intmain(){extern c;//static int a;//printf("%d\n", a); //1.自动初始化为0/*static int arr1[3];*///2.延长了局部变量的生命周期int i =5;while(i--){test();//不加static 11111 加了12345 }//fun1(); //3.static 修饰函数只能在本文件中使用//printf("%d\n", c);//4.static修饰全局变量也只能在本文件中使用return0;}
逗号表达式vs三目操作符
- C语言中的三目运算符表达式格式为:a ? b : c ,其规则为:
当a为真的时候返回b的值,否则返回c的值
- 逗号表达式:exp1, exp2, expp3, … , expN-1, expN表达式的值为最后一个表达式的值,从左向右计算
getchar()
#include<stdio.h>intmain(){char ch=0;while((ch=getchar())!=EOF){if((ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z')){printf("YES\n");}elseprintf("NO\n");getchar();//清除"\n"}return0;}
const
const修饰指针变量的时候
- cosnt 放在*号的左边const int *** 修饰的指针指向的内容,保证指针所指向的内容不会被修改但**指针本身可以修改
- const 放在*号右边 int const 修饰的是指针本身,保证指针*自己不可以被修改**,但是指向的内容可以修改
voidtest1(){int a =3;int b =2;int*p =&a;*p =20;//a=20 p=2 b=2
p =&b;}voidtest2(){int a =3;int b =2;constint*p =&a;/**p = 20;*/
p =&b;//a=3 b=2 p=2 //const保护了a变量不会被修改}voidtest3(){int a =3;int b =2;int*const p =&a;*p =20;//a=20 b=2 p=20 //const保护p不会被修改//p = &b;}intmain(){//test1();//无const//test2();//const 在*左边 test3();//const 在*右边return0;}
操作符
左移,右移
移位操作符的操作数只能是整数,不要移位负数
- 左移
左边抛弃、右边补0
int num=1;
num<<1;//num本身的值没有改变
- 右移> 1. 算术右移:左边的值用符号位补,右丢弃> 2. 逻辑右移:左补零,右丢弃
位操作符
& 按位与 :2个为1才是一
| 按位或:二个二进位有一个为1时,结果位就为1
^ 按位异或:两个相应bit位相同,则结果为0,否则为1
逻辑操作符
intmain(){int i =0, a =0, b =2, c =3, d =4;/*i = a++ && ++b && d++;*///运算完a++为假之后就不再运算后面的
i = a++||++b||d++;//没运算d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return0;}
隐式类型转换
整形提升
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
整形提升是按照变量的数据类型的符号位来提升的
//负数的整形提升char c1 =-1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111//正数的整形提升char c2 =1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001//无符号整形提升,高位补0
发生运算的时候也会发生整形提升
intmain(){char ch =1;printf("%u\n",sizeof(ch));//unsigned int 说明原码反码补码是一样的%uprintf("%u\n",sizeof(+ch));//整形提升printf("%u\n",sizeof(-ch));return0;}
整数中有多少个比特位1
intmain(){int num =3;int count =0;for(int i =0; i <32; i++){if(num&(1<< i))
count++;}printf("%d\n", count);}//优化,数据的二进制比特位中有几个1,循环就循环几次,而且中间采用了位运算,处理起来比较高效intmain(){int num =-1;int i =0;int count =0;//计数while(num){
count++;
num = num&(num -1);}printf("二进制中1的个数 = %d\n", count);return0;}
两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
/*
思路:
1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1
2. 统计异或完成后结果的二进制比特位中有多少个1即可
*/#include<stdio.h>intcalc_diff_bit(int m,int n){int tmp = m^n;int count =0;while(tmp){
tmp = tmp&(tmp-1);
count++;}return count;}intmain(){int m,n;while(scanf("%d %d",&m,&n)==2){printf("%d\n",calc_diff_bit(m, n));}return0;}
获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列*
intmain(){int a =3;for(int i =31; i >=1; i-=2){printf("%d",(a>>i)&1);}printf("\n");for(int i =30; i >=0; i -=2){printf("%d",(a>>i)&1);}return0;}
水仙花数
intmain(){int i =0;for(i=0; i<=999999; i++){int count =1;int tmp = i;int sum =0;//判断i是否为水仙花数//1. 求判断数字的位数while(tmp/10){
count++;
tmp = tmp/10;}//2. 计算每一位的次方和
tmp = i;while(tmp){
sum +=pow(tmp%10, count);
tmp = tmp/10;}//3. 判断if(sum == i)printf("%d ", i);}return0;}
分支和循环
for
for(初始化语句; 循环条件; 自增或自减){
语句块
}都能进行忽略
switch
intmain(){int day =0;scanf("%d",&day);//switch语句中表达式的类型只能是:整形和枚举类型switch(day){//case语句后一般放整形结果的常量表达式或者枚举类型,枚举类型也可以看成是一个特殊的常量case1:printf("MON\n");//break//1.循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。//2.它可用于终止 switch 语句中的一个 case。//3.switch的每个case之后如果没有加break语句,//当前case执行结束后,会继续执行紧跟case中的语句。break;case2:printf("TUS\n");break;default://都不是就走这printf("error\n");break;case3:
printf(" hhhh");//如果default没有break会走到这里}}
猜数字游戏
#include<stdlib.h>#include<time.h>//猜数字游戏//猜1-100的数字//有菜单的voidmenu(){printf("####1.play#####\n");printf("####0.play#####\n");}voidgame(){int n =0;//C 库函数 int rand(void) 返回一个范围在 0 到 RAND_MAX 之间的伪随机数。//RAND_MAX 是一个常量,它的默认值在不同的实现中会有所不同,但是值至少是 32767。int rund_num =rand()%100+1;while(1){printf("输入1一个数字!\n");scanf_s("%d",&n);if(rund_num == n){printf("对了\n");}elseif(rund_num < n){printf("big\n");}else{printf("small\n");}}}intmain(){int input=0;// /* 初始化随机数发生器 */srand((unsignedint)time(NULL));do{menu();printf("请输入:\n");scanf_s("%d",&input);switch(input){case1:game();break;case0:printf("退出游戏!\n");break;default:printf("输入错误请重新输入!\n");break;}}while(input);return0;}
else
else会与最近的if进行匹配
while与for
#include<stdio.h>intmain(){//int i = 0;//while (i <= 5){// i++;// printf("%d\n", i);// if(3==i)// //break; // 终止循环 0,1,2// continue;// 去到判断条件 0,1,2,3,4,5////}//for (i = 0; i <= 5;i++){// // if (i == 3){// break;// 0, 1, 2// continue; // 0,1,2,4,5// }// printf("%d\n", i);//}return0;}
do while
intmain(){/*int i = 5;
do{
printf("%d \n", i);
} while (i < 5);
*///int i = 5;//do{// if (3 == i){// break;// printf("%d \n", i);// }//} while (i < 5);int i =5;do{if(3== i){continue;printf("%d \n", i);}}while(i <5);return0;}
二分查找
//1.找中间值//2.与key值比较,大的mid-1//3.左右都是下标值intmain(){int arr[]={1,2,3,4,5};int key =5;int left =0;int right =sizeof(arr)/sizeof(arr[0])-1;int mid =0;while(left <= right){
mid =(left + right)>>1;if(arr[mid]> key){
right = mid -1;}elseif(arr[mid]< key){
left = mid+1;}else{break;}}if(arr[mid]== key){printf("找到了\n");}else{printf("找不到\n");}return0;}
函数
形参
- 函数()里面的变量,形参只有在函数被调用的时候才会实例化(分配内存)
- 形参在调用完之后就会销毁了,只在函数中存在
- 形参实例化后相当于实参的一份临时拷贝
实参
- 真实传给函数的参数,叫实参。
- 实参可以是:常量、变量、表达式、函数等。
- 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
- 形参和实参在不同的函数中,即不同的作用域,因此形参和实参可以同名
函数的声明与定义
声明
- 告诉编译器有个函数,名字叫什么,返回值叫什么
- 声明放在使用之前,先声明再使用
- 声明一般放在头文件中
定义
- 对函数的实现
递归
递归条件
- 存在限制条件,当满足条件的时候,便不再递归
- 每次递归越来越接近这个条件
递归容易栈溢出
字符串逆序
//编写一个函数 reverse_string(char * string)(递归实现)//实现:将参数字符串中的字符反向排列,不是逆序打印。//要求:不能使用C函数库中的字符串操作函数。//比如 ://char arr[] = "abcdef";//逆序之后数组的内容变成:fedcba//非递归 left right指针一头一尾相互交换直到相遇交换结束//void reverse_string(char arr[])//{// char* left = arr;// char* right = arr + strlen(arr) - 1;// while (left < right)// {// char tmp = *left;// *left = *right;// *right = tmp;//// right--;// left++;// }// printf("%s\n", arr);//}//递归voidreverse_string(char* arr){int len =strlen(arr);char tmp =*arr;//保存第一个字符
arr[0]=arr[len-1]//拿到最后一个字符
arr[len-1]='\0';//在最后一个字符放\0if(strlen(arr +1)>=2)//大于2个字符就换过来reverse_string(arr +1);
arr[len-1]= tmp;}intmain(){char arr[]="abcdef";reverse_string(arr);printf("%s\n", arr);}
//计算一个数的每位之和(递归实现)////写一递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和//例如,调用DigitSum(1729),则应该返回1 + 7 + 2 + 9,它的和是19//输入:1729,输出:19intDigitSum(int n){if(n >9)returnDigitSum(n /10)+ n %10;elsereturn n;}intmain(){int num =1729;int ret=DigitSum(num);printf("%d\n", ret);}
6进制转化
//6进制转换//递归 //1.n==0作为结束条件//2.不断递归/6 到底 再printf(n%6)//#include<stdio.h>//void Printf(int n)//{// if (n == 0);// else// {// Printf(n / 6);// printf("%d", n % 6);// }//}////int main()//{// int n = 0;// scanf("%d", &n);// Printf(n);// return 0;//}//非递归//1.取除6的余数放到数组中//2.逆序打印出来//int main()//{// int n,i = 0;// int arr[50];// scanf("%d", &n);// while (n)// {// arr[i++] = n % 6;// n /= 6;// }// for (i--; i >=0; i--){// printf("%d", arr[i]);// }//}
最大公约数/最小公倍数
//最大公约数求法//欧几里得的辗转相除法://大数除以小数取余数(相当于模运算),直到余数为零时//(也即模运算为零时)的除数(也即模数)就是最大公约数,该算法时间复杂度约为O(logN)。////求最小公倍数的方法:原始数据的乘积除以最大公约数////两个数的乘积等于这两个数的最大公约数和最小公倍数的积。#include<stdio.h>intmain(){int n =0;int m =0;scanf("%d%d",&n,&m);//设max是最大公约数int max = n>m?m:n;//放的是小数//设min是最小公倍数int min = n>m?n:m;//放的是大数while(1){//2个数除到小数能整除if(m%max==0&& n%max ==0){break;}
max--;}while(1){//用大数除到2个都能整除if(min%m ==0&& min%n==0){break;}
min++;}printf("%d\n", max+min);return0;}////int main()//{// long long n = 0;// long long m = 0;// long long k = 0;// scanf("%lld %lld", &n, &m);// long long a = n;// long long b = m;// 最大公约数b// while (k = a%b)// {// a = b;// b = k;// }// printf("%lld\n", b + m*n / b);//// return 0;//}
求阶乘
intfactorial(int x){int ret =1;if(x ==0)
ret =1;elseif(x >1)
ret = x*factorial(x-1);return ret;}intmain(){int a =6;int ret =1;for(int i =1; i <= a; i++){
ret = ret*i;}printf("%d", ret);printf("%d",factorial(6));}
数组
- 数组在内存是连续存放的
- 数组通过下标来访问
数组名
数组名一般是数组首元素的地址
例外
sizeof(arr)//计算的是整个数组的大小&数组名 取的是数组的地址
sizeof()数组总大小
strlen()有效元素个数
intmain(){int arr[10]={0};for(int i =0; i <=9; i++){printf("arr[%d]=[%p]\n", i,&arr[i]);}}#include<stdio.h>intmain(){int*p =NULL;int arr[10]={0};return0;}
p=&arr 是错误的 左边类型是int* 右边是 int(*)arr[10];
二维数组
//二维数组 2是行 3是列int arr[2][3]={{1,2,3},{4,5,6}};int arr1[][3]={{1,2,3}};//行可以省略,列不能省略//*(aa + 1)) 第二行去了 aa//二维数组存储也是连续的int i =0;for(i =0; i <2; i++){int j =0;for(j =0; j <3; j++){printf("arr[%d][%d]=[%p] ", i, j,&arr[i][j]);}printf("\n");}//二维数组行列转化intmain(){int a[3][2]={{2,4},{5,6},{7,8}};int b[2][3]={0};for(int i =0; i <3; i++){for(int j =0; j <2; j++)//这2个for循环用原来a的{
b[j][i]= a[i][j];//行列中数字替换}}}
冒泡排序
voidBubbleSort(int arr[],int sz){//总共要排的趟数for(int i =0; i < sz-1; i++){//每一趟的排序 排升序 排一趟要排的数减少一个for(int j =0; j < sz -1- i; j++){if(arr[j]>arr[j+1]){int tmp = arr[j];
arr[j]= arr[j+1];
arr[j+1]= tmp;}}}}//优化//void BubbleSort(int arr[], int sz)//{// 总共要排的趟数// for (int i = 0; i < sz-1; i++){// 每一趟的排序 排升序 排一趟要排的数减少一个// int flag = 1; //默认进来就是有有序的// for (int j = 0; j < sz - 1 - i; j++){// if (arr[j]>arr[j+1]){// int tmp = arr[j];// arr[j] = arr[j+1];// arr[j+1] = tmp;// flag = 0;// }// // }// if (flag == 1)// break;// }//}
指针
指针是内存最小单元的编号就是地址
平时的指针是指指针变量,用来存放地址的变量
特点
- 指针是用来存放地址的,地址是唯一标识一块地址空间的
- 指针的大小32位平台是4个字节,64位平台是8个字节> 32位机器上32根地址线,地址是32个0/1组成的地址序列所以(2^32/1024Kb/1024Mb/1024Gb==4Gb)空闲进行编址
指针类型的意义
- 指针类型决定了指针向前或向后走一步的距离是多大
- 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
intmain(){int a =3;int*int_p =&a;//这里将a(4个字节)第一个字节放入p中,p就是一个指针变量char*char_p =(char*)&a;printf("%p\n",&a);printf("%p\n", int_p);printf("%p\n", int_p+1);printf("%p\n", char_p);printf("%p\n", char_p+1);//两个指针相减,指针必须指向一段连续空间//减完之后的结构代表两个指针之间相差元素的个数return0;}
野指针
指向的位置是不可知的(随机的,不正确的,没有明确限制的)
造成野指针的原因
int*p;
p =20;//1.没有初始化指针,默认为随机值int arr[5]={0};int*p = arr;for(int i =0; i <6; i++){
p++;//2.指针越界}//3.指针所指向的内存给释放掉了
避免野指针
- 指针使用前检查有效性并初始化
- 小心越界访问
- 指针指向的内存释放前把指针置空
- 避免返回局部变量的地址
二级指针
int a =10;int*pi = a;int**ppi =πprintf("%p\n",&ppi);printf("%p\n",&pi);printf("%d\n", pi);printf("%d\n",*ppi);
- 二级指针能接受指针数组传参
指针数组
int*arr[5]={0};//int *[5] 类型
数组指针
int*arr[5];//[]优先级高于* 这里是指针数组int(*arr)[5];//arr是一个指针变量,指向的是一个大小为5的整形数组int(*parr3[5])[4]//表示存着4个分别指向数组空间大小为5的指针数组
函数指针数组
(*(void(*)())0)();//把0强制转为void (*)(),就是在0处放了一个void (*)()的地址,*解引用找到这个函数//后面()就是传参int arr[10]//这个数组的类型是 int [10] 去掉名字就是类型int(*p)(int)(int)=&add;//函数指针//函数指针 typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型int(*arr[5])();//函数指针数组intadd(int x,int y){return x+y;}intsub(int x,int y){return x-y;}int(*p1)(int,int)=add;int(*p2)(int,int)=sub;//转移表int(*p3[2])(int,int)={add,sub};//函数指针数组int ret=p3[0](1,2)//3
计算器改进版
intadd(int x,int y){return x+y;}intsub(int x,int y){return x-y;}voidcal(int(*p)(int,int)){int x;int y;int ret;printf("请输入!\n");scanf("%d%d",&x,&y);
ret=p(x,y);}intmain(){int input=0;scanf("%d",&input);switch(input){case1:cal(add);break;case2:cal(sub);break;}}
字符指针
char str1[]="hello";char str2[]="hello";constchar*str3 ="hello";constchar*str4 ="hello";if(str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");//not sameif(str3 == str4)printf("str3 and str4 are same\n");//sameelseprintf("str3 and str4 are not same\n");
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
指向函数指针数组的指针
voidtest(){printf("test\n");}intmain(){void(*p)()= test;//函数指针void(*parr[5])();//函数指针数组
parr[0]= test;void(*(*pparr)[5])()=&parr;//指向函数指针数组指针// *pparr 是一个指针指向[5]数组 void(*)()类型return0;}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
sqort
voidqsort(void*base,size_t num,size_t width,int(__cdecl *compare)(constvoid*elem1,constvoid*elem2));intint_compare(constvoid*e1,constvoid*e2){return*(int*)e1 -*(int*)e2;//>0 e1>e2 先强制转化为int*}voidprint(int arr[],int sz){for(int i =0; i < sz; i++){printf("%d ", arr[i]);}}voidtest1(){int arr[]={9,8,7,6,5,4,3,2,1};int sz =sizeof(arr)/sizeof(arr[0]);qsort(arr, sz,sizeof(arr[0]), int_compare);print(arr, sz);}structSTU{char name[10];int age;};//void *可以传递任何类型的参数,但不能运算voidstu_name(constvoid*e1,constvoid*e2){returnstrcmp(((structSTU*)e1)->name,((structSTU*)e2)->name);}voidstu_age(constvoid*e1,constvoid*e2){return((structSTU*)e1)->age -((structSTU*)e2)->age;}voidtest2(){structSTU people[3]={{"xiaoming",30},{"xiaozhang",15},{"dagou",35}};int sz =sizeof(people)/sizeof(people[0]);qsort(people, sz,sizeof(people[0]), stu_age);}intmain(){//test1();test2();return0;}
用回调函数实现通用冒泡排序
//比较函数intint_compare(constvoid*e1,constvoid*e2){return*(int*)e1 -*(int*)e2;//>0 e1>e2 先强制转化为int*}//自定义一个bubbleSort 模拟实现sqort//void qsort(void *base, size_t num,//size_t width, int(__cdecl *compare)(const void *elem1, const void *elem2));voidSwap(char*num1,char*num2,int width){int i =0;//交换宽度次//09 00 00 00 和 08 00 00 00 一个一个字节交换for(i =0; i < width; i++){char*tmp =*num1;*num1 =* num2;*num2 = tmp;
num1++;
num2++;}}voidbubbleSort(void*base,size_t num,size_t width,int(*compare)(constvoid*,constvoid*)){size_t i =0;for(i =0; i < num; i++){for(size_t j =0; j < num - i -1; j++){//以1字节的char+width 就可以指向数组的元素if(int_compare((char*)base + j*width,(char*)base +(j +1)*width)>0){Swap((char*)base + j*width,(char*)base +(j +1)*width, width);}}}}voidtest3(){int arr[]={9,8,7,6,5,4,3,2,1};int sz =sizeof(arr)/sizeof(arr[0]);bubbleSort(arr, sz,sizeof(arr[0]), int_compare);print(arr, sz);}intmain(){//test1();//test2();test3();return0;}
指针相关的练习
数组名
- sizeof(arr)计算的是整个数组的大小 ,sizeof(arr)要单独才表示整个数组大小
- &arr 取的是整个数组的地址 int (*)[ ] 运算也是整个数组运算
- 除此之外数组名都表示首元素地址
intmain(){int a[]={1,2,3,4};printf("%d\n",sizeof(a));//数组名a单独放在sizeof内部,数组名表示整个数组,计算的是整个数组的大小printf("%d\n",sizeof(a +0));//a表示首元素的地址,a+0还是首元素的地址,地址的大小是4/8字节printf("%d\n",sizeof(*a));//a表示首元素的地址,*a 就是首元素 ==> a[0] ,大小就是4//*a <==> *(a+0) <==> a[0]printf("%d\n",sizeof(a +1));//a表示首元素的地址,a+1是第二个元素的地址,大小就是4/8printf("%d\n",sizeof(a[1]));//a[1] 就是第二个元素 - 4printf("%d\n",sizeof(&a));//&a - 数组的地址 - 4/8 - int(*)[4]printf("%d\n",sizeof(*&a));//*&a - &a是数组的地址,对数组的地址解引用拿到的是数组,// 所以大小时候16//printf("%d\n", sizeof(a));//16printf("%d\n",sizeof(&a +1));//4/8 &a是数组的地址,&a+1 是数组的地址+1,//跳过整个数组,虽然跳过了数组,//还是地址 4/8printf("%d\n",sizeof(&a[0]));//4/8printf("%d\n",sizeof(&a[0]+1));//第二个元素的地址 4/8char arr[]={'a','b','c','d','e','f'};printf("%d\n",strlen(arr));//随机值printf("%d\n",strlen(arr +0));//随机值//printf("%d\n", strlen(*arr));//*arr - 'a' - 97 - err//strlen就以为传进来的'a'的ascii码值97就是地址//printf("%d\n", strlen(arr[1]));//'b' - 98 - errprintf("%d\n",strlen(&arr));//随机值printf("%d\n",strlen(&arr +1));//随机值printf("%d\n",strlen(&arr[0]+1));//随机值char arr1[]={'a','b','c','d','e','f'};//6//[a b c d e f]char arr[]="abcdef";//7//[a b c d e f \0]printf("%d\n",strlen(arr));//6printf("%d\n",strlen(arr +0));//6//printf("%d\n", strlen(*arr));//err //printf("%d\n", strlen(arr[1]));//err printf("%d\n",strlen(&arr));//6printf("%d\n",strlen(&arr +1));//随机值printf("%d\n",strlen(&arr[0]+1));//5//printf("%d\n", sizeof(arr));//7 字节//printf("%d\n", sizeof(arr + 0));//arr是首元素的地址 4/8//printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素 1//printf("%d\n", sizeof(arr[1]));//arr[1]就是第二个元素 1//printf("%d\n", sizeof(&arr));//&arr 是数组的地址,数组的地址也是地址,就是4/8字节//printf("%d\n", sizeof(&arr + 1));//&arr 是数组的地址,&arr+1 是跳过整个数组后的地址 4/8//printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1 是第二个元素的地址,4/8constchar* p ="abcdef";printf("%d\n",strlen(p));//6printf("%d\n",strlen(p +1));//5//printf("%d\n", strlen(*p));//err//printf("%d\n", strlen(p[0]));//errprintf("%d\n",strlen(&p));//随机值printf("%d\n",strlen(&p +1));//随机值printf("%d\n",strlen(&p[0]+1));//5//printf("%d\n", sizeof(p));//p是一个指针变量 4/8//printf("%d\n", sizeof(p + 1));//p+1 是字符b的地址 4/8//printf("%d\n", sizeof(*p));//1//printf("%d\n", sizeof(p[0]));//p[0]-->*(p+0) --> *p 1//printf("%d\n", sizeof(&p));//4/8//printf("%d\n", sizeof(&p + 1));//printf("%d\n", sizeof(&p[0] + 1));}
- 二维数组
int a[3][4]={0};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a[0][0]));printf("%d\n",sizeof(a[0]));//a[0]是第一行的数组名,数组名单独放在sizeof内部printf("%d\n",sizeof(a[0]+1));//4/8//arr[0]是第一行的数组的数组名,并没有单独放在sizeof内部,也没有&,所以arr[0]表示首元素的地址//就是第一行这个数组第一个元素的地址//a[0] + 1就是第一行,第二个元素的地址printf("%d\n",sizeof(*(a[0]+1)));//*(a[0] + 1)就是第一行第二个元素 4printf("%d\n",sizeof(a +1));//4/8//数组名a,并没有单独放在sizeof内部,也没有&,所以a表示首元素(第一行)的地址//所以a+1,就是第二行的地址//int(*)[4]//a+1 -> &a[1]printf("%d\n",sizeof(*(a +1)));//*(a+1)就是第二行 - 16//*(a + 1) --> a[1]printf("%d\n",sizeof(&a[0]+1));//4/8//a[0]是第一行的数组名//&a[0] 拿到的是第一行的地址//&a[0]+1,就是第二行的地址//int(*)[4]printf("%d\n",sizeof(*(&a[0]+1)));//16//*(&a[0] + 1) - 第二行 -- a[1]//*(&a[0]+1) --> *(&a[1]) --> a[1]printf("%d\n",sizeof(*a));//a表示首元素(第一行)的地址//*a - 第一行 - 第一行的数组名//*a -> *(a+0) ->a[0]printf("%d\n",sizeof(a[3]));//a[3]假设存在,就是第四行的数组名,sizeof(a[3]),就相当于把第四行的数组名单独放在sizeof内部
structTest{int Num;char*pcName;short sDate;char cha[2];short sBa[4];}*p;//已知,结构体Test类型的变量大小是20个字节intmain(){
p =(structTest*)0x100000;printf("%p\n", p +0x1);//00100014 1*16^1+0*16^0=20 p+0x1相当于加上一个结构体的大小printf("%p\n",(unsignedlong)p +0x1);//00100001 printf("%p\n",(unsignedint*)p +0x1);//00100004 整形指针+1 跳过4return0;}
intmain(){int a[4]={1,2,3,4};int*ptr1 =(int*)(&a +1);int*ptr2 =(int*)((int)a +1);printf("%x,%x", ptr1[-1],*ptr2);return0;}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4Z0CNgU-1674720201669)(C:\Users\75935\Desktop\csdn照片\指针练习1.png)]
intmain(){int a[5][5];int(*p)[4];
p = a;printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);return0;}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6hOCcjC5-1674720201670)(C:\Users\75935\Desktop\csdn照片\指针-指针.png)]
intmain(){char*c[]={"ENTER","NEW","POINT","FIRST"};char**cp[]={c+3,c+2,c+1,c};char***cpp = cp;printf("%s\n",**++cpp);printf("%s\n",*--*++cpp+3);printf("%s\n",*cpp[-2]+3);printf("%s\n", cpp[-1][-1]+1);return0;}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9mTFYzHW-1674720201670)(C:\Users\75935\Desktop\csdn照片\字符指针.png)]
结构体
结构是一些值的集合,这些值叫做成员变量,每个成员变量可以是不同类型
结构体在传参的时候传地址
- 函数传参的时候,参数是需要压栈的,传一个结构体对象时,参数压栈开销比较大,会导致性能下降
#include<string.h>structStudent{char name[10];int age;char ID[5];}two;//声明类型并且定义结构体变量twostructNode{structNode* next;structStudent Stu;};voidPrint(structStudent*p){printf("%s\n", p->name);//结构体指针通过->访问结构体成员}voidPrint1(structStudent*p1){printf("%s\n", p1->name);}voidprint2(structStudent p2){printf("%s\n", p2.name);}intmain(){structStudent one={"xiaoming",18,00001};//定义结构变量并初始化structNode node1 ={NULL,"xiaoming",18,00001};//结构体可以嵌套初始化structStudent three;
three.age =20;//通过.结构体变量访问结构体成员strcpy(three.name,"xiaoming");Print(&three);//结构体传参Print1(&one);print2(one);return0;}
结构体定义
//结构体structbook{char name[20];char author[30];int pirce;}book1,book2;//全局structbook book3;//全局intmain(){structbook book4;//局部}//匿名结构体 只能使用一次,这里只能定义s1,s2struct{char name[30];int age;}s1, s2;struct{int a;int b;char c;}s1;struct{int a;int b;char c;}*sp;intmain(){//ps = &s1;//是错误的 匿名结构体虽然里面的变量是一样的,编译器认为是2个结构体}structNode{int date;//struct Node n; error 会死循环 每个n里面含有一个date又一个n,n里面又有...};structNode{int date;structNode* node;};//error 先使用才定义typedefstruct{int date;
Node* node;}Node;//正确的typedefstructNode{int date;
Node *node;}Node;
结构体内存对齐
规则
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
structS1{char c1;int i;char c2;};structS2{char c1;char c2;int i;};structS3{double d;char c;int i;};structS4{char c1;structS3 s3;double d;};intmain(){printf("%d\n",sizeof(structS1));//12printf("%d\n",sizeof(structS2));//8printf("%d\n",sizeof(structS3));//16printf("%d\n",sizeof(structS4));//32return0;}
为什么会存在结构体对齐?
结构体的内存对齐是拿空间来换取时间的做法
- 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常
- 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
默认对齐数修改
//#pragma pack(4)//设置默认对齐数为4//struct S1//{// char c1;// int i;// char c2;//};////#pragma pack() //取消设置默认对齐数,还原为默认////int main()//{// printf("%d\n", sizeof(struct S1));// return 0;//}
offsetof 计算结构体某变量相对于首地址的偏移
#include<stddef.h>//offsetof 计算结构体某变量相对于首地址的偏移structS1{char c1;int i;char c2;};intmain(){printf("%d\n",offsetof(structS1,c1));//0printf("%d\n",offsetof(structS1, i));//4printf("%d\n",offsetof(structS1, c2));//8return0;}
位段
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
structtest{int _a :2;//占用2个比特位int _b :5;int _c :10;int _d :30;//共47 6bytes};intmain(){printf("%d\n",sizeof(structtest));//8bytesreturn0;}
位段理解
structS{char a :3;char b:4;char c :5;char d :4;};structS s ={0};intmain(){
s.a =10;//01010
s.b =12;//01100
s.c =3;//0011
s.d =4;//00100return0;}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-973Negtm-1674720201671)(C:\Users\75935\Desktop\csdn照片\位段理解.png)]
位段跨平台问题
- int 位段被当成有符号数还是无符号数是不确定的。
- 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的
枚举
枚举特点
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
//枚举enumday{
mon,//0 枚举常量
tues,//1
wed,
thur,
fir,
sat,
sun,};enumColor{
RED =1,
GREEN =2,
BLUE =5,};//枚举应用enumOption{
EXIT,//0
ADD,//1
SUB,//2
MUL,//3
DIV//4};voidmenu(){printf("******************************\n");printf("**** 1. add 2. sub ****\n");printf("**** 3. mul 4. div ****\n");printf("**** 0. exit ****\n");printf("******************************\n");}intmain(){int day1 = mon;printf("%d\n", day1);enumColor clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。int input =0;do{menu();printf("请选择:>");scanf("%d",&input);switch(input){case ADD://加法break;case SUB:break;case MUL:break;case DIV:break;case EXIT:break;}}while();}
联合(共用体)
union Un
{char c;int i;};//联合的成员是共用同一块内存空间的,这样一个联合变量的大小,//至少是最大成员的大小。intmain(){union Un un;printf("%d\n",sizeof(un));//4printf("%p\n",&(un.c));//0074FC78printf("%p\n",&(un.i));//0074FC78return0;}
大小端
intcheck_sys(){union U
{char c;int i;}u;
u.i =1;//返回1 - 小端//返回0 - 大端return u.c;}intmain(){int a =1;//0x 00 00 00 01//低---------------------> 高//01 00 00 00 - 小端存储//00 00 00 01 - 大端存储////char* pc = (char*)&a;//if (*pc == 1)//{// printf("小端\n");//}//else//{// printf("大端\n");//}/*union U
{
char c;
int i;
}u;
u.i = 1;
if (u.c == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}*/if(check_sys()==1){printf("小端\n");}else{printf("大端\n");}return0;}
联合体大小计算
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
union Un1
{char c[5];int i;};union Un2
{short c[7];int i;};printf("%d\n",sizeof(union Un1));//8printf("%d\n",sizeof(union Un2));//16
栈帧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHBHbV51-1674720201671)(C:\Users\75935\Desktop\csdn照片\栈帧1.png)]
调试
- Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
- Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用
数组越界死循环
Debug,release下把i放到arr前面不会出现这个问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FtxpngHk-1674720201672)(C:\Users\75935\Desktop\csdn照片\调试死循环问题.png)]
解释
局部变量i和数组arr都是存储在栈区中的,栈区有个特点先使用高地址的空间再使用低地址的空间,而数组的存储是由低地址到高地址往下连续存续的,当越界访问arr[12]时的地址刚好与i的地址重合的,把i的值也改为0,所以程序死循环了(数组的越界需要停下来才能检查出来,这里死循环进行)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nmeVlOLr-1674720201672)(C:\Users\75935\Desktop\csdn照片\死循环解释.png)]
OJ题1
字符串各种输入
while(gets(str))while(scanf_s("%d%d",&y,&m)!=EOF)
喝汽水
1.20元首先可以喝20瓶,此时手中有20个空瓶子
2. 两个空瓶子可以喝一瓶,喝完之后,空瓶子剩余
empty/2(两个空瓶子换的喝完后产生的瓶子)+ empty%2(不够换的瓶子)3. 如果瓶子个数超过1个,可以继续换,即重复2*/intmain(){int money =0;int total =0;int empty =0;scanf("%d",&money);//方法1
total = money;
empty = money;while(empty>1){
total += empty/2;
empty = empty/2+empty%2;}//方法2if(total<=0){return toatal;}else{return money*2-1;}return0;}
杨辉三角
intmain(){int arr[10][10]={0};int i =0;for(i =0; i <10; i++){int j =0;for(j =0; j <= i; j++){if(j ==0)
arr[i][j]=1;if(i == j)
arr[i][j]=1;if(i >=2&& j >=1)
arr[i][j]= arr[i -1][j -1]+ arr[i -1][j];}}for(i =0; i <10; i++){int j =0;for(j =0; j <= i; j++){printf("%d ", arr[i][j]);}printf("\n");}return0;}
奇数偶数互换
#include<stdlib.h>//调整数组使奇数全部都位于偶数前面voidDepart_Arry(int arr[],int size){int left =0;int right = size -1;while(left < right){//从前到后找偶数while(left < right && arr[left]%2!=0){
left++;}//从后到前找奇数while(left < right && arr[right]%2==0){
right--;}//交换if(left < right){int tmp = arr[left];
arr[left]= arr[right];
arr[right]= tmp;}}}//打印数组voidPrint(int arr[],int size){for(int i =0; i < size; i++){printf("%d ", arr[i]);}}intmain(){int arr[]={2,3,4,6,5,8,7,10,9,1};int size =sizeof(arr)/sizeof(arr[0]);Depart_Arry(arr, size);Print(arr, size);return0;}
杨氏矩阵
intfind(int a[][4],int x,int y,int num){int i =0;int j =y -1;//最右边开始找while(j >=0&& i<x){if(a[i][j]< num)//大向下找
i++;elseif(a[i][j]< num)//小向左找
j--;elsereturn1;}return0;}intmain(){int a[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};int ret =find(a,4,4,16);if(ret ==1){printf("找到啦\n");}elseprintf("找不到\n");return0;}
左旋n个字符
//字符串左旋//实现一个函数,可以左旋字符串中的k个字符。//例如://ABCD左旋一个字符得到BCDA//ABCD左旋两个字符得到CDAB#include<stdio.h>charLeft_hand(char* arr,int k){int right =0;int i =0;int len =strlen(arr);//求出字符串长度char ret;while(k){
ret = arr[0];for(i =0; i < len; i++){if(3== i){
arr[i]= ret;}if(i <=2){
arr[i]= arr[i +1];}}
k--;}}//4.voidLeft_hand(char* arr,int k){int i =0;int len =strlen(arr);for(i =0; i < k; i++){//1.保留第一个字符char tmp =*arr;//2.把后序的字符依次向前移动int j =0;for(j =0; j < len -1; j++){*(arr+j)=*(arr + j+1);}//3.把保存的第一个字符放在最后*(arr+len-1)= tmp;}}//2.voidleftRound(char* src,int time){int len =strlen(src);int pos = time % len;//断开位置的下标char tmp[256]={0};//更准确的话可以选择malloc len + 1个字节的空间来做这个tmpstrcpy(tmp, src + pos);//先将后面的全部拷过来strncat(tmp, src, pos);//然后将前面几个接上strcpy(src, tmp);//最后拷回去}//3.voidreverse_part(char*str,int start,int end)//将字符串从start到end这一段逆序{int i, j;char tmp;for(i = start, j = end; i < j; i++, j--){
tmp = str[i];
str[i]= str[j];
str[j]= tmp;}}voidleftRound(char* src,int time){int len =strlen(src);int pos = time % len;reverse_part(src,0, pos -1);//逆序前段reverse_part(src, pos, len -1);//逆序后段reverse_part(src,0, len -1);//整体逆序}intmain(){char arr[]="ABCD";int k =0;scanf_s("%d",&k);Left_hand(arr, k);printf("%s\n", arr);return0;}
一个字符串是否为另外一个字符串旋转之后的字符串
voidreverse(char* l,char* r){assert(l && r);while(l < r){char tmp =*l;*l =*r;*r = tmp;
l++;
r--;}}voidleft_move(char* arr,int k){assert(arr);int len =strlen(arr);
k %= len;reverse(arr, arr+k-1);//逆序左边reverse(arr+k, arr+len-1);//逆序右边reverse(arr, arr+len-1);//逆序整个字符串}//1.intis_left_move(char arr1[],char arr2[]){int len =strlen(arr1);int i =0;for(i =0; i < len; i++){leftRound(arr1,1);if(strcmp(arr1, arr2)==0)return1;//是}return0;//不是}//2.intfindRound(constchar* src,char* find){char tmp[256]={0};//用一个辅助空间将原字符串做成两倍原字符串(里面包含所有旋转情况)strcpy(tmp, src);//先拷贝一遍strcat(tmp, src);//再连接一遍returnstrstr(tmp, find)!=NULL;//看看找不找得到}intmain(){char arr1[]="AABCD";char arr2[]="BCDAA";int ret =is_left_move(arr1, arr2);if(ret ==1){printf("相等!\n");}elseprintf("不相等!\n");}
OJ题2
有序判断
#include<stdio.h>//输入://5//1 6 9 22 30//输出:sortedintmain(){int n =0;int arr[50]={0};scanf("%d",&n);int i =0;int flag1 =0;int flag2 =0;for(i=0; i<n; i++){scanf("%d",&arr[i]);if(i>0){if(arr[i]>arr[i-1])
flag1 =1;elseif(arr[i]<arr[i-1])
flag2 =1;}}//flag1 和 flag2 都为1是乱序的if(flag1+flag2 >1)printf("unsorted\n");elseprintf("sorted\n");return0;}
删除指定的数字
#include<stdio.h>//输入:6 //1 2 3 4 5 9//4//1 2 3 5 9intmain(){int n =0;int arr[50]={0};int del =0;scanf("%d",&n);int i =0;for(i=0; i<n; i++){scanf("%d",&arr[i]);}scanf("%d",&del);//要删除的元素int j =0;for(i=0; i<n; i++){if(arr[i]!= del){
arr[j++]= arr[i];}}for(i=0; i<j; i++){printf("%d ", arr[i]);}return0;}
有序合并
//5 6//1 3 7 9 22//2 8 10 17 33 44//输出://1 2 3 7 8 9 10 17 22 33 44#include<stdio.h>intmain(){int n =0;int m =0;int arr1[100]={0};int arr2[100]={0};//输入scanf("%d %d",&n,&m);int i =0;for(i=0; i<n; i++){scanf("%d",&arr1[i]);}for(i=0; i<m; i++){scanf("%d",&arr2[i]);}//处理int j =0;
i =0;while(i<n && j<m){if(arr1[i]< arr2[j]){printf("%d ", arr1[i]);
i++;}else{printf("%d ", arr2[j]);
j++;}}if(i == n){for(; j<m; j++){printf("%d ", arr2[j]);}}else{for(; i<n; i++){printf("%d ", arr1[i]);}}return0;}//2.我写的#include<stdio.h>intmain(){int n,m;scanf("%d%d",&n,&m);int arr1[n];int arr2[m];int tmp[n+m+1];int i=0,j=0,z=0,a=0,b=0;for(i=0;i<n;i++)scanf("%d",&arr1[i]);for(j=0;j<m;j++)scanf("%d",&arr2[j]);while(a<n&&b<m){if(arr1[a]<arr2[b]){
tmp[z++]=arr1[a++];}else{
tmp[z++]=arr2[b++];}}while(a<n){
tmp[z++]=arr1[a++];}while(b<m){
tmp[z++]=arr2[b++];}for(int i=0;i<n+m;i++){printf("%d ",tmp[i]);}return0;}
复习时候的错题
填空题的错误1-10
1.int i=3;int k=(i++)+(i++)+(i++);printf("%d %d",k,i); k=9,i=43个i++同时运算
2.设x、y均为float型变量,则以下不合法的赋值语句是 b
A)++x; B)y =(x %2)/10;
C)x *= y +8; D)x = y =0;float是可以进行++,--运算的,不能进行取模运算
3.int i =010, j =10, k =0x10;printf("%d, %d, %d\n", i, j, k);// 8=1*8^1+0*8^0 1 0 16//从左往右计算的,左边是最高次4.字符0的ASCii是48,大写字母比小写字母少325.int k=0;while(k=0) k=k-1;
则以下说法中正确的是 C 。
C)循环体语句一次也不执行 //while(表达式要为真的才进去)6.x=-1;do{ x=x * x;}while(!x);//此程序段 只执行一次//不管怎么样子,先把do执行了再去while 7.int n =0;while(n++<=1);printf("%d\n", n);//n=3 把循环在n=2的时候判断完之后就把n变为3了8.在C语言中,引用数组元素时,其数组下标的数据类型允许是:整型常量或整型表达式
9.若有二维数组a[m][n],则数组中a[i][j]之前的元素个数为 i*n+j
//a[m][n] 默认表示有m+1行,n+1列10.字符数组打印
char a[]="acbde";printf("%s", a[0]);//错误printf("%s", a);//对puts(a);//对
填空题错误
经典问题
1.斐波那契数列
//递归版本intfibonacci(int n){if(n ==1|| n ==2)return1;returnfibonacci(n -1)+fibonacci(n -2);}//非递归版本intfibonacci1(int n){int i =1;int a =1;int b =1;int last =0;if(n <=2)return1;else{for(i =3; i <= n; i++){
last = a + b;a = b;b = last;}}return last;}
2.素数
//判断素数//只需要从2-根号m中判断就可以了intmain(){int i =0,n=10;int end =(int)sqrt(n);for(int i =2; i <= end; i++){if(n %2==0)break;}//判断if(i > end)printf("yes!\n");elseprintf("No!\n");return0;}//输出1-100素数int i =0;int count =0;for(i =1; i <=100; i++){int j =0;for(j =2; j <=(int)sqrt(i); j++){if(i%j ==0)break;}if(j>(int)sqrt(i){ count++;printf("%d ", i); }}printf("\ncount=%d\n", count);return0;}
3.猴子偷桃问题
//递归版本intpeach(int n,int day){int sum =0;if(n == day)
sum =1;else
sum =2*(peach(n +1, day)+1);//(剩下的+1)*2是前一天的桃子数目return sum;}intmain(){/*int res = 1int sum = 0;
for (int i = 1; i < 5; i++){
sum = (res + 1) * 2;res = sum;}printf("%d", sum);*/int ret =peach(1,5);printf("%d ", ret);return0;}
4.打印图像
//输出 *// ***// *****// *******// *****// ***// *intmain(){//打印上四层int n =4;for(int i =1; i <=n; i++){//打印空格,是上面4行所以k<=4-i//第一行 4个 第二行 3个 所以第n行是n-1个空格for(int k=n-i;k>=1;k--){printf(" ");}//按1,3,5,7打印*for(int j =1; j <=2* i -1; j++)printf("*");printf("\n");}//打印下三层
n =3;for(int i =1; i <=n; i++){//空格是逐渐增加的for(int j =0; j < i; j++){printf(" ");}//2*n - (2 * i-1) 不知道怎么来的for(int j =0; j <2*n -(2* i-1); j++)printf("*");printf("\n");}}//******//*****//****//***//**//*//int main()//{// for (int i = 0; i < 6; i++)// {// for (int j =6-i; j > 0; j--)// {// printf("*");// }// printf("\n");// }//}//// *// **// ***// ****// *****//******//int main()//{// int n = 6;// for (int i = 1; i <=n; i++)// {// //第n行有n-1个空格// for (int k = n - i; k >= 1; k--)// printf(" ");// for (int j =1; j<=i; j++)// {// printf("*");// }// printf("\n");// }//}
5.判断单词个数
intmain(){char ch[100]="I am a boy";int i, count =0, flag =0;for(i =0;(ch[i])!='\0'; i++){if(ch[i]==' ') flag =0;elseif(flag ==0){
flag =1;
count++;}}printf("%d", count);return0;}
6.获取最大值最小值
///试编程从键盘输入10个整数并保存到数组,输出10个整数中的最大值及其下标、最小值及其下标。intmain(){int arr[10];int i =0;for(i =0; i <9; i++){scanf("%d",&arr[i]);}int max=arr[0], min=arr[0];//默认第一个为最大/最小,不是的话就替换int ret_max, ret_min;for(i =0; i <9; i++){int tmp;if(arr[i]> max){
tmp = arr[i];
arr[i]= max;
max = tmp;
ret_max = i;//返回的下标}if(arr[i]< min){
tmp = arr[i];
arr[i]= min;
min = tmp;
ret_min = i;}}printf("max值%d下标%dmin值%d下标%d", max, ret_max, min, ret_min);}
i = 010, j = 10, k = 0x10;
printf(“%d, %d, %d\n”, i, j, k); // 8=181+080 1 0 16
//从左往右计算的,左边是最高次
4.字符0的ASCii是48,大写字母比小写字母少32
5.int k=0;
while(k=0) k=k-1;
则以下说法中正确的是 C 。
C)循环体语句一次也不执行 //while(表达式要为真的才进去)
6.x= -1; do { x=x * x;} while(!x); //此程序段 只执行一次
//不管怎么样子,先把do执行了再去while
7.
int n = 0;while (n++ <= 1);
printf(“%d\n”, n); //n=3 把循环在n=2的时候判断完之后就把n变为3了
8.在C语言中,引用数组元素时,其数组下标的数据类型允许是:整型常量或整型表达式
9.若有二维数组a[m][n],则数组中a[i][j]之前的元素个数为 i*n+j
//a[m][n] 默认表示有m+1行,n+1列
10.字符数组打印
char a[] = “acbde”;
printf(“%s”, a[0]);//错误
printf(“%s”, a);//对
puts(a);//对
## 填空题错误
```c
经典问题
1.斐波那契数列
//递归版本intfibonacci(int n){if(n ==1|| n ==2)return1;returnfibonacci(n -1)+fibonacci(n -2);}//非递归版本intfibonacci1(int n){int i =1;int a =1;int b =1;int last =0;if(n <=2)return1;else{for(i =3; i <= n; i++){
last = a + b;a = b;b = last;}}return last;}
2.素数
//判断素数//只需要从2-根号m中判断就可以了intmain(){int i =0,n=10;int end =(int)sqrt(n);for(int i =2; i <= end; i++){if(n %2==0)break;}//判断if(i > end)printf("yes!\n");elseprintf("No!\n");return0;}//输出1-100素数int i =0;int count =0;for(i =1; i <=100; i++){int j =0;for(j =2; j <=(int)sqrt(i); j++){if(i%j ==0)break;}if(j>(int)sqrt(i){ count++;printf("%d ", i); }}printf("\ncount=%d\n", count);return0;}
3.猴子偷桃问题
//递归版本intpeach(int n,int day){int sum =0;if(n == day)
sum =1;else
sum =2*(peach(n +1, day)+1);//(剩下的+1)*2是前一天的桃子数目return sum;}intmain(){/*int res = 1int sum = 0;
for (int i = 1; i < 5; i++){
sum = (res + 1) * 2;res = sum;}printf("%d", sum);*/int ret =peach(1,5);printf("%d ", ret);return0;}
4.打印图像
//输出 *// ***// *****// *******// *****// ***// *intmain(){//打印上四层int n =4;for(int i =1; i <=n; i++){//打印空格,是上面4行所以k<=4-i//第一行 4个 第二行 3个 所以第n行是n-1个空格for(int k=n-i;k>=1;k--){printf(" ");}//按1,3,5,7打印*for(int j =1; j <=2* i -1; j++)printf("*");printf("\n");}//打印下三层
n =3;for(int i =1; i <=n; i++){//空格是逐渐增加的for(int j =0; j < i; j++){printf(" ");}//2*n - (2 * i-1) 不知道怎么来的for(int j =0; j <2*n -(2* i-1); j++)printf("*");printf("\n");}}//******//*****//****//***//**//*//int main()//{// for (int i = 0; i < 6; i++)// {// for (int j =6-i; j > 0; j--)// {// printf("*");// }// printf("\n");// }//}//// *// **// ***// ****// *****//******//int main()//{// int n = 6;// for (int i = 1; i <=n; i++)// {// //第n行有n-1个空格// for (int k = n - i; k >= 1; k--)// printf(" ");// for (int j =1; j<=i; j++)// {// printf("*");// }// printf("\n");// }//}
5.判断单词个数
intmain(){char ch[100]="I am a boy";int i, count =0, flag =0;for(i =0;(ch[i])!='\0'; i++){if(ch[i]==' ') flag =0;elseif(flag ==0){
flag =1;
count++;}}printf("%d", count);return0;}
6.获取最大值最小值
///试编程从键盘输入10个整数并保存到数组,输出10个整数中的最大值及其下标、最小值及其下标。intmain(){int arr[10];int i =0;for(i =0; i <9; i++){scanf("%d",&arr[i]);}int max=arr[0], min=arr[0];//默认第一个为最大/最小,不是的话就替换int ret_max, ret_min;for(i =0; i <9; i++){int tmp;if(arr[i]> max){
tmp = arr[i];
arr[i]= max;
max = tmp;
ret_max = i;//返回的下标}if(arr[i]< min){
tmp = arr[i];
arr[i]= min;
min = tmp;
ret_min = i;}}printf("max值%d下标%dmin值%d下标%d", max, ret_max, min, ret_min);}
版权归原作者 GENIETAO 所有, 如有侵权,请联系我们删除。