#include
#include
void main()
{
char t = 32;
char* tp = &t;
printf("%d\n",t);
printf("%d\n",(void*)t);
printf("%p\n",(void*)t);
printf("%p\n",(void*)&t);
printf("%d\n",tp);
printf("%p\n",tp);
}
/**
输出..........
F:\WWW\CTest\smallTrain>main
32
32
0000000000000020
000000000061FE10
6422032
000000000061FE10
*/
ps: 整数类型比如unsigned char,unsigned short,unsigned int,unsigned long,因为地址也是一个16进制的数。另外通过typedef定义的整型也适用于该规则,比如int32_t,int64_t,size_t等
void main()
{
int t = 32768;
short st = t;
long long lt = t;
printf("%ld\n",t);
printf("%ld\n",st);
printf("%lld\n",lt);
printf("%d\n",sizeof(int));
printf("%d\n",sizeof(int32_t)); // typedef int int32_t;
printf("%d\n",sizeof(long));
printf("%d\n",sizeof(int64_t));//typedef long long int64_t
}
/**
F:\WWW\CTest\smallTrain>main
32768
-32768
32768
4
4
4
8
*/
*所以在特殊情况下,我们是可以直接把整数当成指针来用,比如int p=52647 **
ps: 低位数整型(short)赋值给高位数整型(int、long)是安全转换的,当反过来操作的时候就要注意数值溢出的问题了,以下为int赋值给short的溢出转换过程:
整型int占4个字节,变量int t = 32768,在内存中的存储情况如下:
00000000 00000000 10000000 00000000
整型short占2字节,当执行变量short st = t 时,直接截取t的后两个字节,可得到变量st 在内存中的存储情况如下:
10000000 00000000
因为负数以补码的形式存储,所以结果输出-32768。
void main()
{
for (int i = 0; i < 4; i++) {
int p = 0;
static int q = 0;
printf("for:p %p,%d;q %p,%d\n", &p, ++p, &q,++q);
}
}
/**
F:\WWW\CTest\smallTrain>a
for:p 000000000061FE18,1;q 0000000000407030,1
for:p 000000000061FE18,1;q 0000000000407030,2
for:p 000000000061FE18,1;q 0000000000407030,3
for:p 000000000061FE18,1;q 0000000000407030,4
*/
ps: 可以看到局部变量p的地址是不变的,但是值却一直刷新。如果需要用到该变量的地址,要慎重。
出现这种现象的原因:局部变量是放在栈上的,循环结束一次就释放掉p的地址ADDR,下次循环再次申请相同类型变量就又重复使用该地址ADDR,编译器的一种优化策略。
所以对于该程序而言,局部变量p仍然是一个“新鲜”或“新”的变量。
’\0’ 与 0的区别
‘\0’ 与 0 本质上其实是一样的,一个是ASCII码,一个是该ASCII码对应的字符。字符串结束符 ‘\0’ 的ASCII 值正好是 0。所以用 0 判断和用 ‘\0’ 判断,结果一样(在内存中存放一样)。
‘\0’ 就表示将字符 ’0’ 转义,系统遇到 ’\0’ 时输出一个空格,从而表示ASCII码为0的字符(字符串结束符),而数值 0 和这个是一个意思。
‘0’ 与 ‘\0’的区别
打开ASCII表,可以看到 字符’\0’ 对应的十进制是 0,字符’0‘对应的十进制是48
‘0’ 与 0的区别
这个就比较明显了,前者是字符常量,后者是整型常量
‘0’ 与 "0"的区别
这个就比较明显了,前者是字符常量,后者是字符串常量
void main()
{
char a[10] = {'c','p',0,'s',' ','v'};
printf("%p\n", &a);
printf("%s\n", a);
for(int i=0; i<10; i++){
printf("%c ==%d== %p\n", a[i],i,&a[i]);
}
}
/**
F:\WWW\CTest\smallTrain>main
000000000061FE12
cp //FIXME 这里只输出cp,是因为c里面认为 ‘\0’ 为字符串结尾
c ==0== 000000000061FE12
p ==1== 000000000061FE13
==2== 000000000061FE14
s ==3== 000000000061FE15
==4== 000000000061FE16
v ==5== 000000000061FE17
==6== 000000000061FE18
==7== 000000000061FE19
==8== 000000000061FE1A
==9== 000000000061FE1B
*/
从示例中可以得出:
1):数组的地址即是其第一个元素的地址;
2):char a[10] ={’a‘}并不表示所有元素都初始化为字符’a‘,仅仅是第一个元素初始化为字符’a‘,其他元素默认被填充为字符char的零值 ‘\0’,同理 int b[8]={1,2,3,4};等价于int b[8]={1,2,3,4,0,0,0,0};即如果初始化时指定的的元素个数比数组大小少,剩下的元素都回被初始化为数组类型的零值
3):字符数组可以方便地采用字符串直接初始化,比如 char exp[]=“abcdefghijklmn”;
字符串结尾为什么要以\0结尾?
void main()
{
char a[5]="uvxyz";
printf("a:%s\n", a);
for (int i = 0;i<5;i++)
{
printf("i:%d,v:%c\n", i,a[i]);
}
}
/**
F:\WWW\CTest\smallTrain>a
a:uvxyzp
i:0,v:u
i:1,v:v
i:2,v:x
i:3,v:y
i:4,v:z
*/
可以明显看到如果声明的数组大小和字符个数一样,无法被自动填充’\0’,在输出的时候就会出现输出不存在字符的异常情况。总的来说 c,c++等语言以’\0’作为字符串结尾,只是一种确定字符串结束的方式而已。。如果一个字符串中没有’\0’这个结束字符,那么有些函数(比如printf)将不能确定字符串的结束位置在哪儿,从而引起一些不必要的错误,如果for循环访问该数组并不会有任何异常的。
void main()
{
char a[4]="uvx";
char* b;
b=a;
printf("a:%s\n", a);
printf("b:%s\n", b);
int i;
for (i=0;i<4;i++){
printf("[a] i:%d,v1:%c,v2:%c\n",i, a[i],*(a+i)); //*(a+1)并不是增加一个地址单元(在64位机器上是8个字节), 而是加一个内存单元, 具体的长度由指针类型决定
}
for (i=0;i<4;i++){
printf("[b] i:%d,v1:%c,v2:%c\n",i, b[i],*(b+i));
}
}
/**
F:\WWW\CTest\smallTrain>main
a:uvx
b:uvx
[a] i:0,v1:u,v2:u
[a] i:1,v1:v,v2:v
[a] i:2,v1:x,v2:x
[a] i:3,v1:,v2:
[b] i:0,v1:u,v2:u
[b] i:1,v1:v,v2:v
[b] i:2,v1:x,v2:x
[b] i:3,v1:,v2:
*/
从上面可以看出,指针可以以数组的形式使用,即通过b[i]这种形式访问;数组可以通过指针的形式访问,即*(a+i)的形式,原因何在呢?
原因是因为:数组是由有限长度的连续的内存单元组成的,a[i] 与*(a+i)是等价的,在编译时编译器遇上了a[i],会被编译成:*(a+i),下面的例子可能证明这一点。
void testArr(char* x)
{
printf("x szie:%d\n", sizeof(x));
}
void main()
{
char a[4]={'a','s','d'};
printf("a szie:%d\n", sizeof(a));
testArr(a);
}
/**
F:\WWW\CTest\smallTrain>main
a szie:4
b szie:8
*/
从上面的代码可以看出a的长度就是数组的长度4字节,而b的长度是指针的长度8字节(64位机器)。
因此数组的传参有以下三种形式:
//1
void func(int param[4],int size)
{
}
//2
void func(int param[],int size)
{
}
//3
void func(int* param,int size)
{
}
FIXME: 数组是比指针更高一层级的定义, 数组作为形参, 将退化为指针,所以在传参时要把数组长度作为参数传过去,一旦数组退化为指针就无法求数组长度了。
所以有时候在声明结构体指针时会用结构体数组代替,如下:
typedef struct
{
char* name;
int age;
}Person;
#define INIT_PERSON {0}
void testArr(Person* person)
{
printf("name:%s,age:%d\n", person->name,person->age);
}
void main()
{
Person persons[]={INIT_PERSON};
testArr(persons);
}
/**
F:\WWW\CTest\smallTrain>main
name:(null),age:0
*/
typedef struct
{
char *name;
//char name2[30];
int age;
}Person;
void main()
{
Person person={"xgg",20};//正常初始化
printf("name:%s,age:%d\n", person.name,person.age);
Person person1={0};//初始化为0值
printf("name:%s,age:%d\n", person1.name,person1.age);
}
/**
F:\WWW\CTest\smallTrain>main
name:xgg,age:20
name:(null),age:0
*/
2:结构体释放
typedef struct
{
char *name;
//char name2[30];
int age;
}Person;
void freePerson(Person* person)
{
if(person==NULL)
{
return;
}
if(person->name!=NULL){
free(person->name);
person->name=NULL;
}
free(person);
}
void main()
{
char* name="xgkk";
printf("sizeof(Person):%d,strlen(name):%d\n", sizeof(Person),strlen(name));
Person* person=(Person*)malloc(sizeof(Person));
person->name=(char*)malloc(strlen(name));
strcpy(person->name,name);
person->age=20;
printf("name:%s,age:%d\n", person->name,person->age);
//释放
freePerson(person);
printf("name:%s,age:%d\n", person->name,person->age);
person=NULL;
}
/**
F:\WWW\CTest\smallTrain>main
sizeof(Person):16,strlen(name):4
name:xgkk,age:20
name:P?age:10944848
*/
结构体释放不仅钥匙房结构体变量本身,还要是放结构体内部成员,
另外可以看到释放结构体变量person后,还可以访问person,只不过打印内容是错误的,这是因为free函数只是将指针指向的内存归还操作系统了,但是并不改变person的值 。
void main()
{
char* name=(char*)malloc(5);
strcpy(name,"abcd");
printf("name:%s\n", name);
free(name);
printf("name:%s\n", name);
name=NULL;
}
/**
F:\WWW\CTest\smallTrain>main
name:abcd
name:`r
*/
关于free之后,指针的值为何没有改变,仍然还是这个原先堆空间的这个地址,并且可以访问,原因在于free函数仅仅是将malloc申请的内存释放回去,所谓的释放也就是告诉编译器(操作系统),这块内存已经使用完毕,可以收回了。但指针所指向的值并不会发生改变。就可以比方说,你租了一套房子,到期后,房子收回归还房东,而此时你可能还拿着房子的钥匙,这个时候你虽然可以继续访问这个房子(内存),但已经不属于你,是非法的。也可能有新的租客入驻更改房子的内置,也可能还是这个样子。取决于不同的房东(编译器)和租客(内容),这也是为什么要求在释放指针变量后要赋值为NULL,可以防止误访问。
这就是free释放内存后,指针内地址仍然存在,但有时还可以访问,有时候访问输出乱码或输出其他值的原因。
int testArray(int arr[], int num)
{
for (int i = 0; i < num; i++)
{
arr[i] = i;
}
}
void main()
{
int arr[5];
testArray(arr,5);
for (int i = 0; i < 5; i++)
{
printf("arr[%d]:%d\n", i, arr[i]);
}
}
/**
F:\WWW\CTest\smallTrain>main
arr[0]:0
arr[1]:1
arr[2]:2
arr[3]:3
arr[4]:4
*/
typedef struct {
char** value;
int len;
} TestArray;
TestArray* return_array()
{
TestArray* arr = (TestArray*)malloc(sizeof(TestArray));
arr->len=5;
arr->value=(char**)malloc(sizeof(char*)*arr->len);;
char* name = "abcdefghijk";
for(int i =0; i<arr->len; i++)
{
//char* value = (char*)calloc(i+2,sizeof(char));
char* value = (char*)malloc((i+2)*sizeof(char));
memcpy(value, name, i+1);
printf("1 value[%d]:%s,len:%d\n",i,value,strlen(value));
value[i+1]='\0';
printf("2 value[%d]:%s,len:%d\n",i,value,strlen(value));
arr->value[i] = value;
}
return arr;
}
void main()
{
TestArray* arr = return_array();
for (int i = 0; i < arr->len; i++)
{
printf("arr[%d]:%s\n", i, arr->value[i]);
free(arr->value[i]);
arr->value[i] = NULL;
}
free(arr);//..... 释放
arr=NULL;
}
/**
F:\WWW\CTest\smallTrain>main
1 value[0]:abp,len:3
2 value[0]:a,len:1
1 value[1]:abp,len:3
2 value[1]:ab,len:2
1 value[2]:abc,len:3
2 value[2]:abc,len:3
1 value[3]:abcd,len:4
2 value[3]:abcd,len:4
1 value[4]:abcde,len:5
2 value[4]:abcde,len:5
arr[0]:a
arr[1]:ab
arr[2]:abc
arr[3]:abcd
arr[4]:abcde
*/
2:直接申请区一块内存
char* return_array1()
{
char* res = (char*)malloc(6*sizeof(char));//此时res=[]
strcpy(res, "abcd");//注意 "abcd" = "abcd\0"
res[5] = '\0';
return res;
}
void main()
{
char* str = return_array1();
printf("res:%s,len:%d\n",str,strlen(str));
free(str);
str=NULL;
}
/**
F:\WWW\CTest\smallTrain>main
res:abcd,len:4
*/
char *c和char c[]区别
C 函数参数 char **s与char *s[]
static和const
static和const区别