编程过程中遇到的错误:指针数组中写入字符串

本文深入探讨C语言中指针数组的使用误区,通过实际编程案例解析,揭示了未正确初始化指针数组导致的内存访问错误。文章对比了结构体数组与指针数组在处理字符串输入时的不同表现,强调了在使用指针数组接收字符串输入前,必须先为其分配足够的内存空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:今天复习结构体数组知识的时候,顺手做了一道编程题,没想到发现了大问题。“指针数组”还是掌握的不好。这篇博客就从那道我发现问题的题目入手,讲解一下学习过程中踩过的坑和自己的课后查漏补缺。


原题目:有3个候选人,每个选民只能投票选一个人,要求编一个统计选票的程序,先后输入备选人的名字,最后输出各人投票结果。(参见谭浩强《c程序设计》第四版p300例9.3)

书上思路:设立一个结构体数组,数组中包含3个元素,每个元素的信息应包括候选人的姓名(字符型)和得票数。输入被选人的姓名,然后与数组元素中的“姓名”比较,如果相同,就给这个元素中的“得票数”成员的值加1.最后输出所有元素的信息。

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
struct Person
{
	char name[20];//候选人姓名
	int sore;//得票数
};
int main()
{
        struct Person leader[3] = { { "aa", 0 }, { "bb", 0 }, { "cc", 0 } };//三位候选人信息
	printf("输入名字:");
	char name[20];//定义字符数组
	for (int i = 0; i < 10; i++)
	{
		scanf("%s", name);
		for (int j = 0; j < 3; j++)
		{
			if (strcmp(name, leader[j].name) == 0)
			{
				leader[j].sore++;
			}
		}
	}

	for (int i = 0; i < 3; i++)//遍历结构体数组,输出每位候选人的姓名和票数
	{
		printf("%s %d\n", leader[i].name, leader[i].sore);
	}
       return 0;
}

书中是采用定义一个字符数组,从而不断输入10个候选人名字的。我想的是采用指针数组的形式:定义大小为10的指针数组,存放输入的10个人的姓名,其他原理同上。

关键代码如下

char* name[10];//定义大小为10的指针数组,存放输入的10个人姓名
for (int i = 0; i < 10; i++)
{
	scanf("%s", name[i]);//输入姓名
	for (int j = 0; j < 3; j++)
	{
		if (strcmp(name[i], leader[j].name) == 0)
		{
			leader[j].sore++;
		}
	}
}

然后发现程序崩溃

问题就出在了scanf("%s", name[i]);这句上。我们想象的是指针数组char* name[10]中每一块内容都存放的是char*指针,这个指针指向每个字符串的首字符地址,所以name[0]、name[1]......的类型都为char*,所以这里输入的时候不加“&”符。但是这里踩了一个大坑,也是编程过程中的一个易错点:当字符串以指针的方式存取时,如char*p="abc",这里定义了指针p,p中存放字符串的首地址,abc其实是存储在常量区的,所以只能“读”不能“写”;其次,按目前的声明,name是一个大小为10的指针数组,sizeof(name)=40,说明它只分配了容纳指针的40个字节,数组中的元素其实都是野指针,并没有指向合法可用的内存,因此在写入字符串之前一定要要动态分配内存,并将指针指向新分配的内存。(如下图所示)


再用一个更具体的例子说明问题(这个代码是其中一位层主的回答https://ask.youkuaiyun.com/questions/389563

#include<stdio.h>
#define N 3
#define len 10
void main()
{
//输入N个字符串
    char * str[N];
    int i;
    for(i=0;i<N;i++)
    {
        printf("输入第%d个字符串\n",i+1);
        scanf("%s",&str[i]);

    }
    for(i=0;i<N;i++)
    {
        printf("\n第%d个字符串:",i+1);
        printf("%s\n",str+i);
    }
}

这里往指针数组中写入字符串是通过 scanf("%s",&str[i]);进行的,通过上面的截图说明还是存在问题。可以看到情况一是正常输出的,但是情况二和三都是输出有一些问题,这是为什么?

答:上面的代码是通过scanf("%s",&str[i]);将字符串写入的实际上它并没有写入每次字符指针指向的空间(6161,6262,6363指向的地方),而是写入了存放字符指针本身所占的4个字节中(即f7c4,f7c8,fc77),当所以每个空间只能写入4个字节的字符串;当输出时,采用 printf("%s\n",str+i);的方式进行输出,也就是&str[i],先找到&str[0]的地址f7c4,然后输出该地址存放的字符串,直至遇到‘\0’;然后再找到&str[1]的地址f7c8,输出该地址存放的字符串,直至遇到‘\0’;依次进行。

以情况一为例:地址f7c4中存放的是一个char*类型的指针,占四个字节,代码中把“aa”字符写入了这四个字节当中,也就是占用了3个字节,输出字符串的时候程序找到&str[0]的地址f7c4,遍历字符串,直至遇到\0,所以输出“aa”截止,“bb”和“cc”的正常输出也是同理。

但是情况二和三就不一样了。以情况二为例来说,“aaaaa”一共占据6个字节,可是&str[0]的地址f7c4时存放指针的空间,只有4个字节,于是在写满这四个字节后,又向下一个空间&str[1]的地址f7c8中写入剩下的一个a和‘\0’,参见-->

随后写入第二个字符串“bbbbb"的时候是找到&str[1]的地址f7c8进行写入,但是前两个字节‘a’和‘\0’会被写入的bb覆盖掉-->

所以最终地址f7c4所在空间的4个字节被“bbbb”占据,最后一个b和‘\0’分别写入了&str[2]的地址f7cc所在空间的第一个和第二个字节中-->

最后再找到&str[2]的地址f7cc,写入“ccccc”,同样,这里的前两个字节又被“cc”覆盖掉-->

最终&str[2]的地址f7cc写入了“cccc”,最后一个‘c’和‘\0’写入了&str[3]的地址f7d0空间的前两个字节-->

字符串输出的时候都是先找到所在地址然后输出,直到遇到‘\0’,于是就出现的情况二的错误输出结果,讲解图例如下:

这也就解释了为什么会出现这样的输出。

总结:网上查询的结果不一定对,一定要亲自验证,调试代码弄懂。

 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值