C语言动态开辟指针的指针问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言动态开辟指针的指针问题相关的知识,希望对你有一定的参考价值。
程序大体如下:
int CVICALLBACK Test (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
char **theStr = NULL;
int count;
int i;
switch (event)
case EVENT_COMMIT:
GetNumTextBoxLines (panelHandle, PANEL_TEXTBOX, &count);//获得文本框1的最大行数
theStr = (char **)malloc(sizeof(char) * count*30);
for(i=0; i<count; i++)
GetTextBoxLine (panelHandle, PANEL_TEXTBOX, i, *(theStr+i));//依次获得每一行
InsertTextBoxLine (panelHandle, PANEL_TEXTBOX_2, i, *(theStr+i));给文本框2添加信息
break;
free(theStr);
theStr = NULL;
return 0;
该程序在调试时报错,我跟了下,theStr的行已经被初始化(即可以看到有count个一维数组),但是每一行都为未定义或者说是未初始化的,请问该问题如何处理呢?
如果你想定议成二维的,需要这样
theStr = (char **)malloc(sizeof(char*) * count);
for(int i = 0; i< count; i++)
theStr[i] = malloc(sizeof(char) *30);
//memset
参考技术A 多维的空间申请需要分开一步一步的来申请。申请的都是连续的空间。多维数组在内存最终还是线性存储的。
[C语言]详解指针合集
文章篇幅较长,如有需要请先收藏❤❤❤
文章目录
一、指针是什么
指针是编程语言中的一个对象,利用地址,它的值将指向电脑存储器中另一个地方的值。并且可以通过地址能找到所需的变量单元,可以说,地址指向该变量单元。通过指针可以找到以它为地址的内存单元
二、指针常见的错误
1.未初始化就使用
代码如下(示例):
#include<stdio.h>
int main()
{
int* p;//局部变量未初始化是随机值
*p = 3;//这里访问了未初始化的空间报错
return 0;
}
2.指针越界访问
代码如下(示例):
int main()
{
int a[10] = { 0 };
int* p = a;
//这个地方 a表示数组名,数组名sizeof(a)和单独&a的时候是指向整个数组的地址,
//其他情况都是首元素地址
for (int i = 0; i <= 10; i++)
{
*p = 1;
p++;
}
return 0;
}
这里注意了,指针越界了其实不会造成影响,但是这里修改了指针指向内容的值会报错
3.有趣的代码+习题
(内含对栈区的一些理解)
猜猜代码是在哪出错
1.
#include<stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
*p = 20;
return 0;
}
所以这里当然在main的时候p就已经是一块随机的空间,可能已经被分配给其他使用,这里在win测试的,这个内存图实际上没多大意义,vs2019,x64中验证的,vs编译器本身为了安全,会对地址进程重新分配,但是要记住以下几点:1.栈区内存的使用习惯:先使用高地址空间,在使用低地址空间;2.数组随着下标的增长地址是由低到高变化的(Linux标准的,也是编译器没有经过任何加工的布局,下道题我们用x86验证,就可以模拟上述规则了),
指针的加减与指针的类型有关,加减1次走过指针的类型大小
void*是不能解引用和进行加减的
2.刚提了一嘴这个类型的题目,那接下来再看一道
来猜猜看结果会如何
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (i = 0; i <= 12; i++)
{
arr[i] = 0;
//printf("%d\\n", arr[i]);
}
return 0;
}
答案:死循环,(vs2019 x86)
3.回归主题,这里回到指针
自行做一下,对一下答案
牢记:sizeof里面得到的结果是类型属性,不是值属性,是不会去计算的。
sizeof(数组名) - 数组名表示整个数组的 - 计算的是整个数组的大小
&数组名 – 数组名表示整个数组,取出的是整个数组的地址
此外的所有数组名都是数组的首元素大小
知道这些后,可以尝试写一下题,答案在后面的注释,超详细,这里地址以32位的计算啦
int a[] = { 1,2,3,4 };
printf("%d\\n", sizeof(a));//16
printf("%d\\n", sizeof(a + 0));//这里的a就是首元素地址,是地址就是4字节(32位)
printf("%d\\n", sizeof(*a));//这里的a也是首元素地址,1的地址被解引用找到1,int的大小就是4
printf("%d\\n", sizeof(a + 1));//4*这里的a也是首元素地址,这里是第二个元素的地址,地址的大小是多少就不用说了吧
printf("%d\\n", sizeof(a[1]));//4 --2的大小,那肯定是4个字节
printf("%d\\n", sizeof(&a));//4 --这里就是整个元素的地址,满足&数组名
printf("%d\\n", sizeof(*&a));//16 -- 这里就是整个数组解引用,拿到整个数组的元素
printf("%d\\n", sizeof(&a + 1));//4 --看下面那张图 &a + 1数组后面的空间的地址
printf("%d\\n", sizeof(&a[0]));//4 -- a[0]是第一个元素,&a[0]第一个元素的地址
printf("%d\\n", sizeof(&a[0] + 1));//4 --a[0]+1是第二个元素,&a[0] + 1第二个元素的地址
//strlen 的作用是从给的地址往后找\\0,计算之前的字符个数
//字符数组
char arr[] = { 'a','b','c','d','e','f' };//6个字符
printf("%d\\n", sizeof(arr));//6 --整个元素的地址单独放在sizeof内部,满足要求,计算整个数组的总大小
printf("%d\\n", sizeof(arr + 0));//4--arr为首元素地址,即第一个元素的地址,字符的地址也是4字节
printf("%d\\n", sizeof(*arr));//1-- 第一个元素'a'放在sizeof内部
printf("%d\\n", sizeof(arr[1]));//1 --第二个元素 'b'
printf("%d\\n", sizeof(&arr));//4--整个数组的地址
printf("%d\\n", sizeof(&arr + 1));//4--下一个整个数组的地址 (类似上面那张图的情况)
printf("%d\\n", sizeof(&arr[0] + 1));//4--&arr[0]的指针类型是int*,加1跳过一个整形,‘b’的地址
printf("%d\\n", strlen(arr));//随机值 --无\\0
printf("%d\\n", strlen(arr + 0));//同理
//printf("%d\\n", strlen(*arr));//'a' -- 出错 这里相当于strlen一个整数,错误的strlen的参数是(const char * str),这里把一个整数当成地址,代码出错
//printf("%d\\n", strlen(arr[1]));//同理
printf("%d\\n", strlen(&arr));//随机值 ,整个数组的地址,值与第一个元素的地址相同,但意义不同,图如下
printf("%d\\n", strlen(&arr + 1));//&arr的类型为char(*)[6],随机值-6 ,整个数组的地址,图如下
printf("%d\\n", strlen(&arr[0] + 1));//&arr[0]的类型为char*,'b'的地址找到\\0就停 ,值为:前面的随机值-1
char arr[] = "abcdef";,//这里是用常量区的"abcdef"初始化了arr,在栈区上开辟了一块空间
printf("%d\\n", sizeof(arr));//7 --算的是整个数组的大小加'\\0'
printf("%d\\n", sizeof(arr + 0));//4 --算的是首元素地址的大小
printf("%d\\n", sizeof(*arr));//1 --arr首元素地址,这里计算'a'的大小
printf("%d\\n", sizeof(arr[1]));//1--‘b'的大小
printf("%d\\n", sizeof(&arr));//4--整个数组的地址的大小
printf("%d\\n", sizeof(&arr + 1));//4--f后面的地址,地址的大小
printf("%d\\n", sizeof(&arr[0] + 1));//地址的大小 4字节
printf("%d\\n", strlen(arr));//6
printf("%d\\n", strlen(arr + 0));//6
//printf("%d\\n", strlen(*arr));//err
// printf("%d\\n", strlen(arr[1]));//err
printf("%d\\n", strlen(&arr));//6 指针有类型差异,结果和 strlen(arr)一样,上面解释过相似的题目
printf("%d\\n", strlen(&arr + 1));//随机值
printf("%d\\n", strlen(&arr[0] + 1));//5
char* p = "abcdef";//"abcdef"是放在常量区当中的,p是在栈区的,p的值指向a的首元素地址
printf("%d\\n", sizeof(p));//* 地址4字节,这里的p是存储这个常量字符串的首元素地址
printf("%d\\n", sizeof(p + 1));//p是char*类型的,加减跳过一个字符,'b'地址4字节 'b'的地址
printf("%d\\n", sizeof(*p));//1 p指向a,解引用拿到a
printf("%d\\n", sizeof(p[0]));//1 'a'的大小
printf("%d\\n", sizeof(&p));//4 * 首元素a的地址的地址
printf("%d\\n", sizeof(&p + 1));//4-- 地址的的大小,这里指向的是指针的指针后面--画图
printf("%d\\n", sizeof(&p[0] + 1));//4--地址的的大小
printf("%d\\n", strlen(p));//6
printf("%d\\n", strlen(p + 1));//5 ,p是char*,往后走一个字节为b的地址
///printf("%d\\n", strlen(*p));//err
//printf("%d\\n", strlen(p[0]));//err
printf("%d\\n", strlen(&p));//随机值,指针的地址
printf("%d\\n", strlen(&p + 1));//随机值,指针的地址跳过char(*)[6],如下图
printf("%d\\n", strlen(&p[0] + 1));//随机值,指针的地址,**下图第二个**
p的地址加一跳过一个p的大小
看完这张图,让我们再来做做下面 的题
int a[3][4] = { 0 };
printf("%d\\n", sizeof(a));//48
printf("%d\\n", sizeof(a[0][0]));//4
printf("%d\\n", sizeof(a[0]));//a[0]为第一行的数组名单独放在sizeof内部 * 16
printf("%d\\n", sizeof(a[0] + 1));//a[0]没有单独放在sizeof内部,所以是第一行第一个元素的地址,这答案是第一行第二个元素的地址 4
printf("%d\\n", sizeof(*(a[0] + 1)));//第一行第二个元素的大小 *4
printf("%d\\n", sizeof(a + 1)); // a这里没有放在sizeof内部,这里表示二维数组的首元素地址(二维数组的首元素是第一行数组的地址) 这里是第二行整体的地址 *4
printf("%d\\n", sizeof(*(a + 1)));//这里相当于a[1]单独放在sizeof内部,就是计算第二行所有元素的大小16
printf("%d\\n", sizeof(&a[0] + 1));//第二行的全部元素,&a[0] + 1就是第二行的地址 4
printf("%d\\n", sizeof(*(&a[0] + 1)));//第二行的全部元素,&a[0] + 1就是第二行的地址,解引用拿到整个元素 *16
printf("%d\\n", sizeof(*a));//16 第一行的全部元素
printf("%d\\n", sizeof(a[3]));//sizeof内部不会真的去计算,a[3]是数组名 16
这边解释一下sizeof,代码在下面
short s = 5;
int a = 4;
printf("%d\\n", sizeof(s = a + 6));//这里的表达式就是不会真的去计算a+6的值,所以这里的值是2
printf("%d\\n", s);//5 不计算的话这里自然不会改变
三.动态内存管理
1.malloc,free,calloc,realloc的基本使用
2.malloc的使用
翻译出来就是在内存当中申请一块连续可用的空间,返回这块空间的指针
*如果开辟成功,则返回一个指向开辟好空间的指针。
*如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是 void ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
👀这里我们写一个代码看看基本使用
int main()
{
int* ptr = (int*)malloc(sizeof(int) * 4);
if (ptr == NULL)
{
return 1;
}
else
{
for (int i = 0; i < 4; i++)
{
ptr[i] = i;
printf("%d ", ptr[i]);
}
}
//使用完一定要记得释放否则就造成内存泄漏
free(ptr);
//ptr在free之后仍然指向开辟的空间,但开辟的空间已经还回去了,所以我们要手动置成NULL;
ptr = NULL;
return 0;
}
在这里说明一下在NULL在c语言是定义成地址为0,在c++则是一个整数0
2.calloc的使用
calloc
这里可以看出参数size_t num为我们要的元素数量,size_t size为每个元素的大小,我们也写一段简单的代码
int main()
{
int* ptr = (int*)malloc(sizeof(int) * 10);
if (ptr == NULL)
{
return 1;
}
else
{
for (int i = 0; i < 4; i++)
{
ptr[i] = i;
printf("%d ", ptr[i]);
}
}
//使用完一定要记得释放否则就造成内存泄漏
free(ptr);
//ptr在free之后仍然指向开辟的空间,但开辟的空间已经还回去了,所以我们要手动置成NULL;
ptr=NULL;
return 0;
}
calloc开辟出来的空间也是在堆区上,同然使用完之后要对内存进行释放,同理也要判断是否能开出了来,并置成NULL
👀这里给大家写一个开不出来的情况
这里我在开2g内存的时候ptr返回NULL,如果这里我们没有对返回值做判断,使用的话就会造成错误,当然,想要开出2g或4g空间,只需要把x86改成64位就可以开辟出来了。
3.realloc
realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,
我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整
其中参数ptr是要调整的内存地址,size为调整之后的大小,注意,realloc有两种情况
第一种:原空间后面有足够大的空间
第二种:原空间后面没有足够大的空间
第一种:
第二种:
👀👀👀当然,如果realloc的内存过大开不了也是会返回NULL,所以我们依旧要对返回值进行处理,以下写一个常用的用法
#include<stdlib.h>
#include<math.h>
int main()
{
int* ptr = (int*)malloc(sizeof(int)* 4);
if (ptr == NULL)
{
return 1;
}
else
{
for (int i = 0; i < 4; i++)
{
ptr[i] = i;
printf("%d ", ptr[i]);
}
以上是关于C语言动态开辟指针的指针问题的主要内容,如果未能解决你的问题,请参考以下文章