C语言指针传递和内存分配
2016-10-24 15:19
405 查看
1.内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。这里和Java有很大区别,Java函数调用的内部的存储单元都是在堆上创建的,所以Java需要虚拟机进行GC。
(在函数中不要返回栈内存,但可以返回动态分配的内存)。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
2.数据类型的本质:
C中没有 byte[] ,所以用usign char*(8bit) 代表 ,其实计算机中并没有char 类型,更准确的说计算机并没有数据类型,数据类型都是编译器定义的,比如 'd'的ascii是100(十六机制的0x64)
内存中存储的0x64(0110 0100),如果你认为它是int,那么它就是100(十进制) ;如果你认为它是char,那么它就是d;如果你认为它是字节,它就是unsign char
3.C的内存模型,指针变量名的本质就是内存地址,以下图为例,区分a+1 和 *a+1,a+1代表的是1002这个内存单元的内容,*a+1存储的是b
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/02/1e7abc5fad1b7134a7c1a8c4a91a7363)
4.区分可打印字符串和不可打印字符串:
char[3] a = {'x','y','z'}; //字符数组
char* a = "xyz";//末尾自动补零,它是可打印字符串
unsigned char* deviceID = "AA\0AAA";
printf("%d",strlen(deviceID)); //输出结果为2,但是真实长度是5,被截断了,是不可打印字符串,如果函数要返回deviceID给调用者,需要把deviceID的真实长度返回。
像Base64编码就是可打印的编码,即编码的结果没有'\0',不会被截断的。ASCII 字符是不可打印的。
5、函数返回指针的几种方法(以字符指针为例char *,特别注意返回不可打印的字符指针的问题)
(1) 在调用函数之前可以获知字符串的长度
(2)在调用函数之前不知道字符串的长度
错误一:C函数调用是在栈上分配的,所以在函数内部定义的局部变量会随着函数调用的结束而出栈销毁,b不会被赋值。即使通过malloc分配内存也是不行的,b一直是NULL
错误二:下面的方法把指针的地址作为参数调用函数,在函数内malloc,用完了由调用者free,但是忽略了字符的返回可能被截断的问题。
正确方法一:
通过函数返回值来传递动态内存,但是这个方法不能解决返回的字符串包含不可打印字符的问题,
需要注意的是返回值只能是动态分配的内存,如果是函数内的局部变量千万不要通过函数返回值,虽然可以返回,但是由于是在栈上分配的,所以可能会被下面的调用覆盖。
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。这里和Java有很大区别,Java函数调用的内部的存储单元都是在堆上创建的,所以Java需要虚拟机进行GC。
(在函数中不要返回栈内存,但可以返回动态分配的内存)。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
2.数据类型的本质:
C中没有 byte[] ,所以用usign char*(8bit) 代表 ,其实计算机中并没有char 类型,更准确的说计算机并没有数据类型,数据类型都是编译器定义的,比如 'd'的ascii是100(十六机制的0x64)
内存中存储的0x64(0110 0100),如果你认为它是int,那么它就是100(十进制) ;如果你认为它是char,那么它就是d;如果你认为它是字节,它就是unsign char
3.C的内存模型,指针变量名的本质就是内存地址,以下图为例,区分a+1 和 *a+1,a+1代表的是1002这个内存单元的内容,*a+1存储的是b
4.区分可打印字符串和不可打印字符串:
char[3] a = {'x','y','z'}; //字符数组
char* a = "xyz";//末尾自动补零,它是可打印字符串
unsigned char* deviceID = "AA\0AAA";
printf("%d",strlen(deviceID)); //输出结果为2,但是真实长度是5,被截断了,是不可打印字符串,如果函数要返回deviceID给调用者,需要把deviceID的真实长度返回。
像Base64编码就是可打印的编码,即编码的结果没有'\0',不会被截断的。ASCII 字符是不可打印的。
5、函数返回指针的几种方法(以字符指针为例char *,特别注意返回不可打印的字符指针的问题)
typedef unsigned char byte; void print_char_string(byte *mychar, int len){ int i=0; for(i=0;i<len;i++){ printf("%c->0x%x;",mychar[i],mychar[i]); } printf("\n"); }
(1) 在调用函数之前可以获知字符串的长度
void test(byte *mychar) { mychar[0] = 'a'; mychar[1] = 'b'; mychar[2] = '\0'; mychar[3] = 'd'; mychar[4] = 'e'; } int main() { byte b[5]={0}; print_char_string(b,5); test(b); print_char_string(b,5); return (0); }
(2)在调用函数之前不知道字符串的长度
错误一:C函数调用是在栈上分配的,所以在函数内部定义的局部变量会随着函数调用的结束而出栈销毁,b不会被赋值。即使通过malloc分配内存也是不行的,b一直是NULL
void test(byte *mychar) { mychar[0] = 'a'; mychar[1] = 'b'; mychar[2] = '\0'; mychar[3] = 'd'; mychar[4] = 'e'; } int main() { byte *b = NULL; test(b); return (0); }
错误二:下面的方法把指针的地址作为参数调用函数,在函数内malloc,用完了由调用者free,但是忽略了字符的返回可能被截断的问题。
void test2(byte *p) { p[0] = 'a'; p[1] = 'b'; p[2] = '\0'; p[3] = 'd'; p[4] = 'e'; } void test(byte **mychar) { *mychar = (byte *)malloc(sizeof(byte) * 5); memset((void*)(*mychar), 0, 5); byte* p = *mychar; test2(p); } int main() { byte *b = NULL; test(&b); print_char_string(b,strlen(b));//a->0x61;b->0x62;,因为b字符串包含了不可打印字符,造成了截断 free(b); return (0); }
正确方法一:
void test2(byte *p) { p[0] = 'a'; p[1] = 'b'; p[2] = '\0'; p[3] = 'd'; p[4] = 'e'; } int test(byte **mychar) { *mychar = (byte *)malloc(sizeof(byte) * 5); memset((void*)(*mychar), 0, 5); byte* p = *mychar; test2(p); return 5; } int main() { byte *b = NULL; int length = test(&b); print_char_string(b,length); free(b); return (0); }
正确方法二:
通过函数返回值来传递动态内存,但是这个方法不能解决返回的字符串包含不可打印字符的问题,byte* test3(){ byte *p = (byte *)malloc(sizeof(byte) * 5); memset((void*)(p), 0, sizeof(byte) * 5); p[0] = 'a'; p[1] = 'b'; p[2] = '\0'; p[3] = 'd'; p[4] = 'e'; return p; } int main() { byte *b = test3(); free(b); return (0); }
需要注意的是返回值只能是动态分配的内存,如果是函数内的局部变量千万不要通过函数返回值,虽然可以返回,但是由于是在栈上分配的,所以可能会被下面的调用覆盖。
相关文章推荐
- C语言指针传递和内存分配
- C语言学习7 :二级指针定义,强制转换,多级指针初步,6级指针构造,错误应用*p=&a,错误应用 二级p2,void型指针的兼容性,malloc函数基本用法,malloc分配空间和堆栈空间的区别,验证malloc函数内存的分配,验证malloc函数的越界,内存泄漏,指针不能返回局部变量地址,内存分配
- 学点 C 语言(23): 数据类型 - 给指针分配内存
- C语言学习7-1:使用6级指针访问一个整数,使用隐式内存分配中转,约瑟夫环
- 【C/C++语言基础学习】在主函数的定义的指针数组、二维数组通过三级指针在被调用函数分配内存
- 函数参数的传递问题(指针的指针)【想在函数中分配内存】(指针做参数,即可做输入参数也可做输出参数)
- 动态内存分配涉及到的二级指针,传递相关
- [C++]动态给传递指针分配空间
- 指针传递内存深入探讨(二)
- 指针传递内存深入探讨(三)
- C 语言中的指针和内存泄漏
- c/c++中指针参数如何传递内存
- c/c++中指针参数如何传递内存
- c/c++中指针参数如何传递内存
- c/c++中指针参数如何传递内存
- c/c++中指针参数如何传递内存
- c/c++中指针参数如何传递内存
- 指针传递内存深入探讨(一)
- c/c++中指针参数如何传递内存
- 利用模版类实现指针内存的分配管理