============================================================================= 涉及到的知识点有:for循环有两种写法、数组、一维数组定义与使用、一维数组的初始化、 如何得到一个一维数组的成员数量、查找出一维数组中成员最大值、查找一维数组的第二大元素的值、 一维数组的逆置、一维数组排序:冒泡排序、二维数组、二维数组的初始化、三维数组初始化、三维数组排序、 字符串与字符数组、字符数组的初始化、字符数组的使用(以及字符数组和字符串的区别)、去除输出字符串结尾处的空格、 现在要去掉字符串最右面的空格,而不能去掉字符串中间的空格呢、随机数产生函数rand与srand、 自动的变种子、控制随机数的范围、用scanf来输入字符串、如何把两次输入的字符串放到新的字符串里去、 scanf缓冲区溢出的危险的解释、字符串的逆置。 ============================================================================= for循环有两种写法:
第一种写法: int i; for(i = 0; i < 10; i++) //这个是标准C写法,该写法可以同时兼容c和c++。 {
}
第二种写法: for(int i = 0; i < 10; i++) //这个是C++写法,该写法有些编译器不行,不支持。 {
}
区别:两种写法均可以,没有对错!
============================================================================= 数组:就是在内存中连续的相同类型的变量空间。
1、一维数组定义与使用 例如: int a[10]; //定义了一个一维数组,有10个成员,每个成员都是int类型,在内存中是连续存放的。 int a[10]; //从a[0]开始到a[9]结束,注意:没有a[10]这个元素 a = 5; //这句话是错误的。 //数组的名字本身是一个常量,不能作为左值的。即不能把一个数组名当一个变量去用!!! //数组名:在c语言中数组名其实就是数组第一个元素的地址,是一个常量。 (该常量是“变化”的,这里面“变化”的意思是:只要数组大小确定,首元素的地址随之确定) //即 a=&a[0] ----------------------------------------------------------------------------- 2、一维数组的初始化 各种各样的写法如下: char a[5]; //等同于随机初始化,随机赋值 char a[5] = {1,2,3,4}; //等同于定义后就初始化了
//默认后续元素为零的简写 char a[5] = {1,2}; //等同于char a[5] = {1,2,0,0};
//把所有成员的值都设为0的简写 char a[5] = {0}; //等同于char a[5] = {0,0,0,0};
int a[] = {1,2,3}; //等同于int a[3] = {1,2,3} int a[] = {1,2,3,4,5,6}; //等同于int a[6] = {1,2,3,4,5,6}; ----------------------------------------------------------------------------- 3、如何得到一个一维数组的成员数量?
linux下的代码如下:
1 #include <stdio.h> 2 3 int main() 4 { 5 int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,};//定义一个一维数组,每个成员都是int类型,在内存中是连续存放的。 6 7 int i; 8 for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) 9 { 10 printf("a[%d] = %d\n", i, a[i]); 11 } 12 //printf("%lu, %lu\n", sizeof(a), sizeof(a[0]));
13 return 0; 14 } ----------------------------------------------------------------------------- 4、一个一维数组,其成员数量是随机的,数组成员的值也是随机的,需要查找出该数组中成员最大值。
linux下的代码如下:
1 #include <stdio.h> 2 3 int main() 4 { 5 int a[] = {-321, 52, 85, -78, 96, 789}; 6 int i; 7 int tmp = a[0]; 8 for (i = 1; i < sizeof(a) / sizeof(a[0]); i++) 9 { 10 if (a[i] > tmp) 11 tmp = a[i]; 12 } 13 printf("max = %d\n", tmp);
14 return 0; 15 } ----------------------------------------------------------------------------- 5、查找一维数组的第二大元素的值,要求只能用一个循环,也不能用循环嵌套。
linux下的代码如下:
1 #include <stdio.h> 2 3 int main() 4 { 5 int a[] = {-321, 52, 85, -78, 96, 789}; 6 7 //默认数组中第一个和第二个成员分别是最大的和第二大的 8 int max = a[0]; 9 int smax = a[1]; 10 if (a[0] > a[1]) 11 { 12 max = a[0]; 13 smax = a[1]; 14 } 15 else 16 { 17 max = a[1]; 18 smax = a[0]; 19 } 20 21 int i; 22 for (i = 2; i < sizeof(a) / sizeof(a[0]); i++) 23 { 24 if (a[i] > max) 25 { 26 smax = max; 27 max = a [i]; 28 } 29 else if (a[i] < max && a[i] > smax) 30 { 31 smax = a[i]; 32 } 33 else 34 { 35 } 36 } 37 38 printf("smax = %d\n", smax);
39 return 0; 40 } ----------------------------------------------------------------------------- 6、一维数组的逆置
vs2017下的代码如下: #include <stdio.h>
int main() { int a[] = { 45, 87, 56, 89, 52, 456, 489, 1235 }; int min = 0; //代表数组的最小下标 int max = 0; //代表数组的最大下标 max = sizeof(a) / sizeof(a[0]) - 1;
while (min < max) { int tmp = a[min]; a[min] = a[max]; a[max] = tmp; min++; max--; } //两个元素交换的思路:要有一个临时的量,把要交换的某一个量先存起来 //int tmp = a[0]; //a[0] = a[7]; //a[7] = tmp; int i; for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) { printf("a[%d] = %d\n", i, a[i]); }
return 0; } ----------------------------------------------------------------------------- 7、一维数组排序:冒泡排序 名词解释:将一个无序的一维数组排序成一个有序的一维数组,遍历这个一维数组,将最大的成员放到最后一个。 怎么把最大的元素放到最后呢?思路是:两个相邻的元素依次比较,把大的放到小的后面。
例如:冒泡排序思路 第一次循环的时候: 12,45,73,55,81,36 12,45,55,73,81,36 12,45,55,73,36,81 这样最大的元素跑到了最后
第二次再循环的时候:就不用管最后一个元素了 12,45,55,73,36 ...... 12,45,55,36,73 这样第二大元素也跑到了最后
第三次再循环的时候:最后2个元素都不用管了 12,45,55,36, ...... 12,45,36,55 这样第三大元素也跑到了最后
依次类推......
再把思路转换成代码。。。。。。
vs2017下的代码如下: #include <stdio.h>
int main() { int a[] = { 32, 85, 98, 12, 56, 47, 654 , -789, 10 }; int i, j; int max = sizeof(a) / sizeof(a[0]);
for (i = 0; i < max; i++) { for (j = 1; j < max - i; j++) { if (a[j - 1] > a[j]) //前面的元素大于后面的元素时,大的放在后面,从小到大,两个元素交换 { //int tmp = a[j - 1]; //a[j - 1] = a[j]; //a[j] = tmp;
int tmp = a[j]; a[j] = a[j - 1]; a[j - 1] = tmp; } } } for (i = 0; i < max; i++) { printf("a[%d] = %d\n", i, a[i]); }
return 0; }
----------------------------------------------------------------------------- 小注意事项: int i = 10; int a[i]; //这种定义数组的方法在过去的c标准中不能被支持,但C99以后的编译器可以支持这种语法,一般最好不要这样写。
int a[10]; //但可以这样写
#define NUM 100 //也可以这样写 int a[NUM];
============================================================================= 二维数组:本质一下子可以定义多个一维数组。
int a1[10]; //a1是一个一维数组,一维数组的数组名是a1。 int a2[2][10]; //a2是一个二维数组,二维数组的数组名是a2,这个二维数组中包含2个一维数组,分别是a2[0]、a2[1](注意a2[0]、a2[1]是一维数组的数组名),a2[0]和a2[1]这2个数组是连续的。 int a3[3][10]; //a3是一个二维数组,二维数组的数组名是a3,这个二维数组中包含3个一维数组,分别是a3[0]、a2[1]、a3[2](注意a3[0]、a3[1]、a3[2]是一维数组的数组名),a3[0]、a3[1]、a3[2]这3个数组是连续的。
再次复习:数组名:在c语言中数组名其实就是数组第一个元素的地址,是一个常量。常量是不可以做左值的。 ----------------------------------------------------------------------------- 对于下面而言: int a2[2][10]; printf("%u, &u, %u\n", sizeof(a2), sizeof(a2[0]), sizeof(a2[0][0])); printf("%p, %p, %p\n", a2, a2[0], &a2[0][0]); //三个值是一样一样的!!!
-->sizeof(a2); //80 a2是二维数组名,sizeof(a2);该句的意思是:二维数组的大小 * * * * * * * * * * * * * * * * * * * *
一维数组 -->sizeof(a2[0]); //40 a2[0]是一维数组名,sizeof(a2[0]);该句的意思是:一维数组的大小 * * * * * * * * * *
二维数组 -->sizeof(a2[0][0]); //4 a2[0][0]是二维数组中的某一元素(注意:也即一维数组的某一元素哦!),sizeof(a2[0][0]);该句的意思是:二维数组中的某一元素的大小(注意:也即一维数组的某一元素的大小) * - - - - - - - - - - - - - - - - - - -
----------------------------------------------------------------------------- 注意:是竖着排列的哦!!! int a4[3][4][10]; //这是一个三维数组,有3个二维数组,二维数组名分别是:a4[0]、a4[1]、a4[2],每个二维数组中有4个一维数组,其中一个二维数组的一维数组的数组名分别是:a4[0][0]、a4[0][1]、a4[0][2]、a4[0][3] 三维数组:本质就是多个二维数组 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ----------------------------------------------------------------------------- 三维数组初始化:要特别特别注意:二维数组有两种输出的排列方式:
例如对于int a5[2][3][4];
- - - - - - - - - - - - -->横着看,是一个二维数组,包含3个一维数组 - - - - - - - - - - - - -->横着看,是一个二维数组,包含3个一维数组
竖着看 竖着看 - - - - - - - - - - - - - - - - - - - - - - - - 是一个二维数组 是一个二维数组
int a5[2][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
int a5[2][3][4] = { 0 };
遍历三维数组的核心代码:(注意:打印的出来的横着看的哦!!!) int i, j, k; for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { for (k = 0; k < 4; k++) { printf("a[%d][%d][%d] = %d,", i, j, k, a[i][j][k]); } printf("\t"); } printf("\n"); }
----------------------------------------------------------------------------- 二维数组的初始化
int a1[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int a2[3][4] = { {1, 2, 3, 4},{5, 6, 7, 8} }; //第三个数组默认置为0 int a3[3][4] = { 0 }; //将所有的成员都初始化为0 int a4[][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, { 8, 5, 9, 7}, {6, 8, 1, 2}}; //一维数组的个数不定
遍历二维数组的核心代码: for (i = 0; i < sizeof(a4) / sizeof(a4[0]); i++) { for (j = 0; j < sizeof(a4[0]) / sizeof(a4[0][0]); j++ ) { printf("a4[%d][%d] = %d\t", i, j, a3[i][j]); } printf("\n"); } ----------------------------------------------------------------------------- 三维数组排序:把整个三维数组的所有成员进行排序
思路:(核心思想就是:降维或者称之为:抽象为一维数组) 1、遍历三维数组后,把这个三维数组所有元素提出放到一个一维数组里面,得到一个一维数组; 2、把这个得到的一维数组进行排序(使用冒泡排序); 3、把排序后的一维数组再放回到原来的三维数组里; 4、为了看的见,再次遍历打印这个新的三维数组。
vs2017下的代码如下: #include <stdio.h>
int main() { int a[2][3][5] = { { {5,1,2,4,89}, {45,21,10,20,22}, {45,210,123,587,456}}, { {52,45,879,410,230}, {456,85,51,65,96}, {87,45,10,21,22}} }; int b[30] = { 0 }; int i, j, k; int index =0; //1、遍历三维数组后,把这个三维数组所有元素提出放到一个一维数组里面,得到一个一维数组; for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { for (k = 0; k < 5; k++) { b[index] = a[i][j][k]; index++; } } } //2、把这个得到的一维数组进行排序(使用冒泡排序); for (i = 0; i < 30; i++) { for (j = 1; j < 30 - i; j++) { if (b[j - 1] > b[j]) { int tmp = b[j-1]; b[j - 1] = b[j]; b[j] = tmp; } } } //3、把排序后的一维数组再放回到原来的三维数组里; index = 0; for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { for (k = 0; k < 5; k++) { a[i][j][k] = b[index]; index++; } } } //4、为了看的见,再次遍历打印这个新的三维数组。 for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { for (k = 0; k < 5; k++) { printf("a[%d][%d][%d] = %d\n", i, j, k, a[i][j][k]); } } }
return 0; }
============================================================================= 字符串与字符数组
字符串定义: 字符串是内存中一段连续的char空间,以 '\0' 结尾。 字符数组定义: 字符数组也是内存中一段连续的char空间哦!(特别注意:字符数组并不) 所以在c语言中,字符串和字符数组有着千丝万缕的联系,特别相似,有时都是通用的哦!
举例子如下: char a[10]; //定义了一个字符类型的数组 a[0] = 'a'; a[1] = 'b'; a[2] = 'c'; a[3] = '\0'; //注意:0就是'\0'的ASCII码哦!!!所以 a[3] = '\0'; 等价于 a[3] = 0; printf("%s\n", a); //输出的是 abc printf("%s\n", "abc"); //输出的是 abc 说明了 char a[10];虽然是一个字符数组,但是它符合c语言中关于字符串的描述。 其实呢,c语言中并没有单独的字符串类型,字符串类型其实就是靠字符数组来表达的。
char a[10] = "aabbcc";//此为简化的写法。其实理应该一个个的赋值那样复杂的写会更好的。初始化数组成员,注意:不能理解成定义的字符数组就等于右边的字符串啊!!! a = "hello"; //不能这样写,给数组名赋值 ----------------------------------------------------------------------------- 字符数组的初始化 举例子如下: 初始化的时候,如果后面的值不写的话,默认都是0。 char a[10] ={ 'a', 'b', 'c'}; //复杂啰嗦的写法,更直观 //a[0] = 'a'; //a[1] = 'b'; //a[2] = 'c'; //a[3] = '\0'; //注意:0就是'\0'的ASCII码哦!!!所以 a[3] = '\0'; 等价于 a[3] = 0; char a[10] = "abc"; //简化写法,更抽象,少写一个是一个,哈哈哈!!! char a[] = "abc"; //注意这个字符数组有4个成员变量哦~
//把一个字符数组所有成员都初始化为0(或叫初始化为空串),有以下两种写法哦~ char a[10] = { 0 }; 或者 char a[10] = { '\0' }; char a[10] = ""; //如果是空串,什么都没有,说明里面只有一个 '\0'(或0) 哦~
超级特别注意:0就是'\0'的ASCII码,即他们俩是等价的,但是具体含义不一样哦~
char a[12] = "hello world"; int i; for (i =0; i < sizeof(a); i++) { printf("%d\n", a[i]); //打印的结果为 104 101 108 108 111 32 119 111 114 108 100 0 注意:空格的ASCII是32,'\0'字符零的ASCII是0。 } ----------------------------------------------------------------------------- 字符数组的使用(以及字符数组和字符串的区别)
在c语言中,没有字符串这种数据类型,可以通过char的数组来替代,字符串就是以0结尾的char的数组。
如果有一个char的数组,但不是以'\0'(字符零或者0)结尾的,那么这个数组就一定不是一个字符串,所以字符串是一种特殊的char的数组。
特别注意:(小心理解有偏差哦!!!)一个char的数组中可以出现多个字符零,但一个字符串中只能有一个字符零。
所以说当我们用一个字符数组的时候,要明确的知道,该字符数组什么时候可以当一个字符串用,什么时候当一个数组使用。
举例子如下: char a[7] = "hello"; a[3] = '\0'; //等价于a[index] = 0;对于字符串来讲,是"hel",对于字符数组来讲,它的成员数量没有变化,还是7个。 //print("%s\n", a); //输出的是:hel
a[3] = 'l'; a[5] = 'a'; a[6] = 'a'; //这个时候a还是一个字符数组,但已经不是一个字符串了,因为结尾没有以0结尾了,不符合c语言对字符串的定义和要求了。 printf("%s\n", a); //输出的是:helloaa烫烫滔窓酏S 后面是乱码哦!!!
----------------------------------------------------------------------------- 去除输出字符串结尾处的空格
vs2017下的代码如下:
#include <stdio.h>
int main() { char a[100] = "hello "; int index = 0; while (a[index]) { if (a[index] == ' ') { a[index] = '\0'; //等价于a[index] = 0; 特别注意:只有单个字符才有对应的ASCII,即对应的是某一数字,而凑巧的是 '\0'字符零的ASCII是0。 break; //还有要注意的是:我们无法在一个字符串上直接加上一个字符,需要通过访问字符串元素的方式去间接的改变字符串中的某一字符的值。 } index++; }
printf("(%s)\n", a); //特别注意:%c 输出单个字符;%s 输出字符串,实际上是输出多个单个字符。
return 0; } ----------------------------------------------------------------------------- 现在要去掉字符串最右面的空格,而不能去掉字符串中间的空格呢? 比如:char a[100] = "hello world "; 思路:不能从前往后数,需要从后往前数,遇到不是空格的字符就把这个字符的下一个字符设置为字符零('\0')或者0。
vs2017下的代码如下:
#include <stdio.h> int main() { char a[100] = "hello world ";//"hello world "; 等价于 { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', ' ', ' ', ' ', '\0' }; //得到字符数组的角标大小 int index = 0; while (a[index]) { index++; }
int i; for (i = index - 1; i >= 0; i--) { if (a[i] != ' ') { a[i + 1] = '\0'; break; } }
printf("(%s)\n", a);
return 0; }
============================================================================= 随机数产生函数rand与srand,这两个函数在stdlib.h里面
rand(); //无参数,是伪随机数,每次产生的随机数是一样的。 srand(); //真随机数的种子,参数是无符号的整数,参数就是种子,只要种子不同,rand产生的随机数就不同;如果种子一样,则rand产生的随机数就一样。
一般而言,这两个函数是配合在一起用的。
vs2017下的代码如下:
#include <stdio.h> #include <stdlib.h>
int main() { int a; int i; srand(58); for (i = 0; i < 5; i++) { a = rand(); printf("%d\n",a); } return 0; }
----------------------------------------------------------------------------- 那么如何让它能自动的变种子呢?即让srand()函数的参数总是在变化,什么东西是不停变化的啊?计算机的时间啊!!!(傻帽哈哈) 这就用到了time函数,time函数是c语言的库函数,功能是得到系统当前时间。那怎么定义呢?需要一个头文件 #include <time.h> time函数的参数可以写空,返回值是一个无符号的整型(时间可没有负的哦!)。如下: unsigned int tm = time(NULL);
vs2017下的代码如下:
#include <stdio.h> #include <stdlib.h> #include <time.h>
int main() { int a; int i; unsigned int tm = time(NULL); srand(tm); for (i = 0; i < 5; i++) { a = rand(); printf("%d\n",a); } return 0; }
----------------------------------------------------------------------------- 由观察可知,我们生成的随机数的范围比较大,那么如何把生成的随机数范围控制住呢?
vs2017下的代码如下:
#include <stdio.h> #include <stdlib.h> #include <time.h>
int main() { int a; int i; unsigned int tm = time(NULL); srand(tm); for (i = 0; i < 5000; i++) { a = rand() % 101; //只生成从0到100之间的随机数,方法是:取余啊!对101取余。 printf("%d\n",a); } return 0; }
============================================================================= 用scanf来输入字符串
例如: char a[100] = { 0 }; //要养成一个好习惯:定义一个数组的时候,同时把成员初始化为0。 scanf("%s", a); //用户通过键盘可以把一个字符串写入一个char的数组
注意:scanf并不会直接读入用户最后输入完成的回车键,而是最后自补一个0(该0是以单个字符存在的哦,即字符零),这样才是完整的字符串,scanf并不检查参数的char数组的下标,用户输入多少,scanf就往数组里面放多少,一旦用户输入的过多,会导致内存错误。
通过scanf输入的时候,最后按的是一个什么键?答:回车键,scanf会把回车键认为是输入完成,而不是字符串的内容。 而且scanf认为回车和空格都代表输入完成哦。
当字符数组的成员数量小于用户在键盘输入字符的数量之后,scanf并不会自动处理,而是把用户输入的所有字符都放入了数组,导致了数组溢出了,内存出错,程序崩溃。
vs2017下的代码如下:
#include <stdio.h> #include <stdlib.h> #pragma warning(disable:4996)
int main() { char a[100] = { 0 }; scanf("%s", a); printf("%s\n", a); return 0; }
----------------------------------------------------------------------------- 如何把两次输入的字符串放到新的字符串里去呢?(即将两个字符串进行合并)
vs2017下的代码如下:
#include <stdio.h> #include <stdlib.h> #pragma warning(disable:4996)
int main() { char a[100] = { 0 }; scanf("%s", a);
char b[100] = { 0 }; scanf("%s", b);
char c[200] = { 0 };
int index_a = 0; while (a[index_a]) { c[index_a] = a[index_a]; index_a++; }
int index_b = 0; while (b[index_b]) { c[index_a + index_b] = b[index_b]; index_b++; }
printf("%s\n", c);
return 0; }
----------------------------------------------------------------------------- scanf缓冲区溢出的危险的解释!
微软为什么不让我们用scanf呢?因为scanf是只要你敢输入,那么它就敢放,直到把你的数组撑爆!把程序搞坏了!
再次说明:数组名:在c语言中数组名其实就是数组第一个元素的地址,是一个常量。 char a[10] = { 0 }; printf("%p, %p\n", a, &a); //打印结果是一样的,所以没必要画蛇添足 scanf("%s", a); 等价于 scanf("%s", &a[0]);
----------------------------------------------------------------------------- "abc " 空格不是'\0' 空格也有ASCII是32
----------------------------------------------------------------------------- 一维数组的逆置 字符串的逆置
字符串为英文时: char a[100] = "I love you sweat heart";
vs2017下的代码如下:
#include <stdio.h>
int main() { char a[100] = "I love you sweat heart"; int index = 0; while (a[index]) { index++; }
int min = 0; int max = index - 1; while (min < max) { char tmp = a[min]; a[min] = a[max]; a[max] = tmp; min++; max--; }
printf("%s\n", a);
return 0; }
----------------------------------------------------------------------------- 当字符串为中文时呢?该如何逆置? char a[100] = "我爱你小宝贝";
在UTF-8模式下,每一个汉字占用3个BYTE。 windows系统默认字符集GBK,在GBK下,一个汉字占用2个BYTE。 小规律:一个汉字的第一个字节总是小于0的一个整数。
我爱你
我 -26 -120 -111
该如何交换呢?
思路:(linux采用的是UTF-8的编码,每一个汉字占用3个BYTE。)
第一个字节和倒数第三个字节交换 第二个字节和倒数第二个字节交换 第三个字节和倒数第一个字节交换
linux下的代码如下:
1 #include <stdio.h> 2 3 int main() 4 { 5 char a[100] = "我爱你小宝贝"; 6 7 int min = 0; 8 int max = 0; 9 while (a[max + 1]) 10 { 11 max++; 12 } 13 14 while (min < max) 15 { 16 char tmp = a[min]; 17 a[min] = a[max - 2]; 18 a[max -2] = tmp; 19 20 tmp = a[min + 1]; 21 a[min + 1] = a[max - 1]; 22 a[max - 1] = tmp; 23 24 tmp = a[min + 2]; 25 a[min + 2] = a[max]; 26 a[max] = tmp; 27 28 max -= 3; 29 min += 3; 30 } 31 32 printf("%s\n", a); 33 34 return 0; 35 }
=============================================================================