(仅个人知识总结,用自己的话写出来的,并非全是对的,无瑕疵的)
DAY3:数组和位运算
知识点1---冒泡排序思想:N个元素 进行n-1轮两两比较,第j轮,进行n-j次运算,大的数往后移
eg:输入10个数,先按小到大排序,再逆序(使用冒泡思想)
知识点2---一些经典例题
经典题(1):定义30个整型元素数组,按顺序分别赋予从2开始的偶数,然后按照顺序,每五个数求一次平均值,再另一个数组输出;
经典题(2)
知识点3---位运算
& 与 两个位均是1,才为1;其他为0;
| 或 两个均为0,才为0,其他为1;
^ 异或 两个相同为0,不同为1;
~ 取反 1则变为0,0则变为1;
<< 左移 左移一位相当于*2 (左移运算,向左进行移位操作,高位丢弃,低位补0);
0000 0001
0000 0010 //<<左移一位
>> 右移 右移一位相当于/2;
eg:取32bit 无符号整数0x12345678的高16bit和低16bit,分别保存在16bit的无符号整数并表示出来。(64位机)
知识点4----------空指针与野指针
#include <stdio.h>
#if 0
1、指针的定义
数据类型* 变量名;
2、指针变量中保存谁的地址, 那么指针就是指向谁
3、在定义指针变量时, *表示标志性的作用, 除此以外, 所有指针前加*都表示指针指向的内存
4、*指针变量, 可以作左值,也可以作右值
5、在32位机:4字节 64位: 8字节
6、指针变量和保存的地址类型不匹配, 保存地址一点问题都没有,但是访问数据会存在问题
7、不要出现野指针, 当指针指向不确定时, 指向NULL
#endif
int main01()
{
int a = 10;
int* p = &a; // * 表示标志性的作用, int 表示保存整型变量的地址
int** q = &p;
printf("a= %d, *p=%d, **q=%d\n", a, *p, **q);
printf("&a = %p, p=%p, *q = %p\n", &a, p, *q);
printf("&p = %p, q=%p \n", &p, q );
printf("&q = %p\n", &q);
return 0;
}
int main02()
{
printf("sizeof(int*) = %ld\n", sizeof(int *));
printf("sizeof(char*) = %ld\n", sizeof(char *));
printf("sizeof(double*) = %ld\n", sizeof(double *));
printf("sizeof(long*) = %ld\n", sizeof(long*));
return 0;
}
int main03()
{
int a = 0x11223344;
char *p = &a;
printf("&a: %p, p=%p\n", &a, p);
printf("a: %#x, *p=%#x\n", a, *p);
return 0;
}
int main()
{
//int *p = 10;
int a;
int *p = NULL; //野指针
*p = 100;
return 0;
}
int a=0;
int *p;
*p=20; //错误,p是野指针
int *q=a;
{
int b=20
q=b;
}
*q=30;
printf("%d %d %d",a,b,*q);
//a ==>0; b???错误 ;*q 野指针==>50;
//因为{}里面是局部函数,出了局部,b没有意义了,q指向的地方虽然是b存在的地方,
//但是已经算是一个随机值了,所以p算是野指针
空指针:指针p并不是指向地址为0的存储单元,而是具有一个确定的值--“空”
空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方,每种指针类型都有一个空指针,而不同类型的 空指针 内部表示可能不尽相同;
知识点5-----------------二维数组小知识
#include <stdio.h>
#if 0
假设 0<=i <=2 0<=j<=3
1、第一数组每个元素的起始地址
(1) &a[0] &a[1] ... &a[i] ---->表示a数组第i+1个元素的起始地址
(2) a a+1 ... a+i ---->表示a数组第i+1个元素的起始地址
2、第二维数组每个元素的起始地址
注意:a[i] 对于第一个数组来说表示某个元素, 但是每个元素又是一个一维数组, 所以a[i]表示每个一维数组的数组名
(1) &a[0][0] &a[0][1] ..... &a[0][j] ----->表示a[0]数组每个元素的起始地址
&a[i][0] &a[i][1] ..... &a[i][j] ------>表示a[i]数组每个元素的起始地址
(2) a[0] a[0]+1 ..... a[0]+j
a[i] a[i]+1 ..... a[i]+j
3、二维数组的起始地址:
&a: 二维数组的起始地址
&a+1: 偏移整个二维数组的大小
ps:
&a: 数组的起始地址
a+i:每一行的起始地址
&a[i]: 每一行第一个元素的起始地址
#endif
int main()
{
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
printf("第一维每个元素的起始地址:\n");
for(int i=0; i<3; i++)
{
printf("&a[%d] = %p\n", i, &a[i]);
}
for(int i=0; i<3; i++)
{
printf("a+%d = %p\n", i, a+i);
}
printf("第二维数组每个元素的起始地址:\n");
for(int i=0; i<3; i++)
{
for(int j=0; j<4; j++)
{
printf("&a[%d][%d]=%p ", i, j, &a[i][j]);
}
printf("\n");
}
for(int i=0; i<3; i++)
{
for(int j=0; j<4; j++)
{
printf("a[%d]+%d=%p ", i, j, a[i]+j);
}
printf("\n");
}
printf("&a: %p\n", &a);
printf("&a+1:%p\n", &a+1);
return 0;
}
二维数组a[3][4]的元素a[i][j]的地址的表达方式:
&a[i][j] a[i]+j *(a+i)+j &a[0][0]+4*i+j a[0]+4*i+j
内容:
a[i][j] *(a[i]+j) *(*(a+i)+j) *(&a[0][0]+4*i+j) (*(a+i))[j]
&a表示二维数组的地址 ,绑定一个二维数组;
a表示首行的地址,绑定一行;
&a[0]表示首行地址,绑定一行;
a[0]表示首行元素地址,绑定一个元素; ====》虽然他们%p的值都相等,但是绑定的长度不一样
a[0][0]表示首行首元素的值;
&a[0][0]表示首行首元素的地址;
a[0] ====>&a[0][0]; a[i] ====>a[i][0];
关于首行元素地址和首行地址:虽然他们%p的值都相等,但是绑定的长度不一样
eg:&a[0[0]+1 ===>&a[0][1] 绑定一个,右移四个字节;
&a[0]+1 =====>&a[1][0] 绑定一行,偏移16个字节;
&a+1 =======>指向数组a的下一个其他位置,偏移一个数组
知识点6-----关于字符串
- %s ----输出的时候也是遇到\0结束;
char str1[20]="hello world!"; printf("%s %s",str1,&str[2]) //输出的是:hello world! llo world //&str[2]表示从第二个(0,1,2)开始输出。与普通数组不同,需要注意!!!!
输出控制符的%s的作用:字符数组/字符串数组的规定就是输出地址代表输出地址所指定的元素和后面的元素,直到遇到\0;、
-
char str[]="abcde"; printf("%s %c\n",&str[2],str[2]); //&str[2]是从第二位输出;str[2]是输出第二位 //===>结果cde c; //%s str[2]====>这样对应会报错 //%d str[2]====>这样输出的是第二位的ASCII马!!!!!) //%d &str[2]====>这样输出的是第二位的地址(这个真的是地址)
%s对应的是地址,%c对应的是变量!!!
指针变量是不能直接赋值的,但是!!!字符串可以直接赋值给字符指针变量!!!
-
char *p="abcd"; //正确; //int *p=1 错误;
原因:双引号做了3件事:
(1)申请了空间(在常量区),存放了字符串
(2)在字符串尾加上了'/0'
(3)返回地址
这里就是 返回的地址 赋值给了 p
字符串常量"abcd"出现在一个表达式中时,"abcd"表达式使用的值就是这些字符所存储的地址(在常量区),而不是这些字符本身。所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。
同样是a数组,char a[10] = “hello”;这种是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理;但是换成char a [10],然后a = “hello”就不行了 “hello”赋值的值是一个地址,而a虽然也有地址,但是这与指针是不一样的,指针的值是地址,而数组的值虽然也是地址,但是却是一个常量,所以不能给常量赋值。
知识点7---------字符串常量和存储
#include <stdio.h>
int main()
{
char *p = "hello world";
*p = '9';//不可以修改 因为p指向字符串常量,不能修改常量的 值
printf("p : %s\n", p);
return 0;
}
#include <stdio.h>
#if 0
注意事项:
1、如果使用指针接收一个常量字符串, 那么字符串存储在常量区, 返回的是字符串的起始地址,
这时不可以通过指针修改字符串的内容
2、在c语言中字符串使用字符数组存储
3、如果使用字符数组接收一个常量字符串, 会将字符串存储到字符数组中, 并且在最后会自动添加尾零
ps :常量区是只读的不能写!!!!
#endif
int main01()
{
char s[1024] = {'h', 'e', 'l','l','o', '\0'};
//char name[32] = "hello";
char name[] = "hello";
char *p = name;
*p = 'H';
printf("name: %s\n", name);
return 0;
}
int main02()
{
char zg[] = "中国";//在linux中一个汉字 3字节
printf("sizeof(zg) = %ld\n", sizeof(zg));
return 0;
}
int main03()
{
char name[32];
printf("请输入字符串:");
scanf("%s", name); //不能获取带有空白键的字符串
printf("name: %s\n", name);
return 0;
}
int main04()
{
char name[32];
printf("请输入字符串:");
gets(name); //将回车键一起读取过来,转成尾零存储, 所以gets函数后面不需要清空缓冲区
printf("name: %s\n", name);
return 0;
}
#if 0
char *fgets(char *s, int size, FILE *stream);
函数功能: 从文件中获取一行字符串
参数说明:
s:存放字符串内存的起始地址
size: 内存的大小
stream: 文件指针 stdin:标准输入 stdout:标准输出 stderr:标准出错
返回值:
成功返回s, 失败返回NULL
ps:假设从键盘输入n个字符,包含回车键,
如果n < size, fgets会将n个字符一起读取,并且在回车键的后面加尾零
如果n >= size, fgets会读取size-1个字符, 在最后一个元素的位置存储尾零
#endif
int main()
{
char name[5];
printf("请输入字符串:");
fgets(name, 5, stdin);
printf("name: %s\n", name);
return 0;
}