一、printf输出int值
void test02(){
int b = 1024;
printf("%o\n",b);//2000
printf("%x\n",b);//400
printf("%X\n",b);//400
printf("%8p\n",&b);//这个8位没有生效,输出的还是0x7ffe6c164f24
printf("%x\n",&b);//6c164f24
printf("%012X\n",&b);//00006C164F24
}
二、int类型数值溢出
void test01(){
int a = 0x7fffffff;
a = a+3;
printf("%d\n",a);//-2147483646
/**
0x7fffffff:0111 1111 1111 1111 1111 1111 1111 1111
+3 : 0000 0000 0000 0000 0000 0000 0000 0011
//高位溢出
1000 0000 0000 0000 0000 0000 0000 0010
1000 0000 0000 0000 0000 0000 0000 0010的原码如下:
1111 1111 1111 1111 1111 1111 1111 1101 + 1 =
1111 1111 1111 1111 1111 1111 1111 1110
首位是符号位,因此这个数值部分位7fff fffe,即这个数的十进制为-2147483646
*/
}
三、常量指针和指针常量
void test01() {
int a = 10;
const int* p = &a;
int b = 20;
p = &b;
//*p = 100;//IDE提示报错,const位于*之前表示p是指向常量的指针,所以不能通过*p修改p指向地址里面的值
int* const p1 = &a;
//p1 = &b;//IDE提示报错,*位于const之前表示p1是指针常量,所以不能修改p1指向的地址,但是可以通过*p1修改p1指向地址里面的值
printf("%d %d\n",*p,*p1);
}
四、auto、volatile、register
/*
auto关键字:https://zhuanlan.zhihu.com/p/348520921
有修饰变量生命周期的作用,也有类型推导的作用
volatile关键字:https://zhuanlan.zhihu.com/p/343688629
有禁止编译器优化,以及刷新变量值作用
register关键字:https://zhuanlan.zhihu.com/p/263575137
*/
五、函数指针
int add(int a,int b) {
return a + b;
}
//返回值类型 (* 自定义类型名称)(参数类型)
typedef int(*FuncName)(int,int);
void test08() {
FuncName fn = add;
int(*Fn)(int, int) = add;
printf("%d %d\n", Fn(100,34),fn(12,22));
}
六、malloc、calloc、realloc
//开辟一个10字节大小的内存
int *b = malloc(10);//不会设置内存值为0
//开辟一个4*sizeof(int)字节大小的内存
int* c = calloc(4, sizeof(int));//会设置内存为0
//对于b扩容,如果开辟的b后面还有剩余内存的话,则会在b后面紧接着开辟内存内存。
//如果没有的话,则会全部重新开启内存。把之前b的内容复制过去
b = (char*)realloc(b,10);
七、字符串数组
void test01() {
//char a[10] = "hello world"; //报错,字符串常量不能直接赋值给字符数组
char* str[] = {"ab","cd","123"};
char* str2[] = { "ab","cd","123" };
char** st1 = str;
/*
打印:ab b cd 123
*st1:表示常量字符串"ab"的首地址
*st1+1:表示常量字符串"ab"的第二个字符值
*(st1+1):表示常量字符串"cd"的首地址
*(st1 + 2):表示常量字符串"123"的首地址
*/
printf("%s %s %s %s\n",*st1,*st1+1,*(st1+1), *(st1 + 2));
}
八、通过内存拷贝获取结构体属性的字符串
typedef struct {
char* str1;
int age;
} person_t;
void test01() {
/*
* 当前默认的结构体对齐的模数是8,str1的类型是char *,age的类型是int,所以sizeof(p)的值16
*/
person_t p = {
.str1 = "hello world",
.age = 23
};
char** str1 = calloc(1, sizeof(char*));
/*
将p在内存中的前8个字节的内容拷贝给二级指针str1。
因为p在内存中的前8个字节的内容是字符串"hello world"在内存中的地址值,
所以这个地方要用二级指针来接收
*/
memcpy(str1, &p, 8);
//*str1将会得到"hello world"字符串首个字符的地址值。即p1与p2的值是相等的。
printf("str1=%s p1=%p p2=%p\n", *str1,*str1, &((*str1)[0]));
for (int i = 0; i < strlen(*str1);i++) {
printf("%c\n",(*str1)[i]);
}
free(str1);
}
九、关于数组别名步长的问题
void test04() {
int a[] = {1,2,3,4,5,6,7,8,9,10};
/*
a是指向数组首元素地址的指针
&a是指向整个数组的首地址
虽然a和&a打印出来的地址值都一样,但是数据类型不一样。
a是元素指针类型,&a是数组指针类型
*/
printf("%p %p\n",a,&a);//000000830F6FF438 000000830F6FF438
/*
000000830F6FF438+4=000000830F6FF43C
000000830F6FF438+28=000000830F6FF460
因为a指针的指针步长为4,&a的指针步长为40(16进制就是28)
*/
printf("%p %p\n", a+1, &a+1);//000000830F6FF43C 000000830F6FF460
/*
sizeof(a)是计算整个数组所占内存空间的大小
sizeof(&a)是计算指针所占空间大小,64位系统,一个地址值所占内存空间大小为8个字节
*/
printf("%d %d\n",sizeof(a),sizeof(&a));//40 8
}
十、指针数组与数组指针
/*
数组指针和指针数组
http://c.biancheng.net/view/335.html
*/
void test05() {
//数组指针
int(*p1)[5] = {0}; //只允许有一个初始值,多个初始值报错
//指针数组
int* p2[5] = {1,2,3};
}
十一、字符串初始化
void test08() {
char h = 'h';
char* hp = &h;
printf("hp=%c\n", *hp);
char *str1 = "hello world";
//*str1 = 'h';
//printf("str1=%c\n",*str1); //抛出异常,字符串常量区的内容不能修改
//报错,buf初始化需要带括号的初始化表达式列表
//char buf[] = str1;
//这种是可以的
char buf1[]="hello world";
//不指定长度,没有0结束符,有多少个元素就有多长,所以这样写在memcpy的时候就会报错。因为如果str1长度大于buf申请的
//长度,那就是非法操作指针了
//char buf[] = {0};
char buf[100] = { 0 };;
memcpy(buf,str1,strlen(str1));
buf[0] = '2';
printf("buf=%s\n", buf);
char buf2[100]={'h','e','w'};//没有赋值的自动以数字0填充
}
十二、字符串结束符0的区别
void test07() {
char buf1[] = {'q','b','c',0,'1','3'};
//字符0是起不到结束字符串的作用
char buf2[] = { 'q','b','c','0','1','3' };
char buf3[] = { 'q','b','c','\0','1','3' };
printf("buf1=%s\n",buf1); //qbc
printf("buf2=%s\n", buf2); //qbc013乱码
printf("buf3=%s\n", buf3);//abc
}