在子函数中改变字符数组的值

本文通过一个具体的C语言示例程序,介绍了如何正确地修改字符数组的内容而不改变其内存地址,对比了直接赋值字符串与使用strcpy函数的区别,并展示了如何初始化及修改字符数组。
#include <stdio.h>
#include <string.h>

void TestFun(char a[100])
{
printf("before addr:%p\n", a); //打印a的地址

//a = "very good"; //错误。不能直接将字符串赋值给字符数组,a的地址改变了。估计是系统为a开辟了新的内存空间。"very good"的生存期是从进程开始到进程结束
//a[0] = "very good"; //错误。a的地址不会改变,可以改变a[0]的数值

strcpy(a, "very well"); //正确

/*
a[0] = 'v'; //正确。逐个赋值
a[1] = 'e';
a[2] = 'r';
a[3] = 'y';
a[4] = ' ';
a[5] = 'g';
a[6] = 'o';
a[7] = 'o';
a[8] = 'd';
a[9] = '\0';
*/

printf("after addr:%p\n", a); //打印a的地址
printf("%s\n", a);
}

int main(void)
{
char TestStr1[60] = {"abchij"};
char TestStr2[] = {"abchij"};
char TestStr3[100] = {65,66,67};

//strcat(TestStr1 , TestStr2);

printf("main before TestStr3:%s\n", TestStr3);
TestFun(TestStr3);
printf("main after TestStr3:%s\n", TestStr3);


getchar();
return 1;
}

在vs2010中的运行结果:
main before TestStr3:ABC
before addr:003EF7A4
after addr:003EF7A4
very well
main after TestStr3:very well

总结:可以用{ printf("after addr:%p\n", a); //打印a的地址 }来打印一个指针、或数组名的地址。

<think> 根据用户需求,我们需要编写一个递归函数convert,将非负整数n转换为字符串,并存储在字符数组str中。同时,在main函数中调用该函数,并输出转换后的字符串。 关键点: 1. 函数原型:void convert(int n, char str[]) 2. 使用全局变量pos来记录当前字符在数组中的位置(因为题目中已经定义了全局变量int pos=0;) 3. 递归思路:先递归处理高位(即n/10),然后再将当前最低位数字转换为字符存入数组。 4. 注意:由于是递归,每次递归调用时,我们先将高位的数字转换完,再转换当前位。因此,递归终止条件是n小于10(即只剩一位),此时直接将该位转换为字符并存储,然后返回。 5. 但是,注意题目要求:非负整数,所以不需要考虑负数情况。 6. 在递归过程中,我们使用全局变量pos来记录当前应该存储字符的位置。每次递归调用后,pos会递增,以便存储下一位。 具体步骤: 递归函数convert: - 如果n大于等于10,则递归调用convert(n/10, str); 这样就会先处理高位数字。 - 然后,将当前最低位数字(n%10)转换为字符,并存入str[pos]位置,然后pos++。 - 注意:递归终止条件可以不用显式写出,因为当n<10时,我们直接处理它(即不进行递归调用,直接存储当前数字字符)。但是,在递归调用之前,我们需要判断是否还有高位,所以用if(n>=10)来递归处理高位。 然而,注意递归的调用顺序:先递归处理高位,然后再处理当前低位。这样就能保证数字的顺序正确。 另外,在存储完所有数字后,我们需要在字符串末尾添加结束符'\0'。但是,递归函数中不便于添加结束符,因为递归过程中我们不知道何时结束。因此,可以在main函数中,调用convert之后,手动在字符数组的末尾添加结束符。 但是,观察题目中已经定义了全局变量pos,并且字符数组str在main中定义,大小为MAX_LEN(11)。在递归过程中,我们使用pos作为索引来存储字符。递归完成后,我们在main函数中给str[pos]赋值为'\0',这样字符串就完整了。 但是,注意:全局变量pos在多次运行程序时可能会保留上一次的,因此每次调用convert之前需要将pos重置为0。但是题目要求不要修改代码框架,而题目中已经定义了全局变量int pos=0;,所以在main函数中,在调用convert之前,我们不需要再重置pos(因为初始为0),但是要注意,如果程序多次调用convert(比如多次输入),那么第二次调用时pos就不是0了,所以题目中只要求一次输入,所以可以这样用。但为了安全,我们可以在main函数中,调用convert之前将pos重置为0(但是题目框架不允许修改,所以我们不能改,而且题目只要求一次调用)。 因此,编写递归函数如下: 递归函数convert: if (n >= 10) { convert(n / 10, str); } // 将当前位数字转换为字符,并存入str str[pos] = n % 10 + '0'; // 将数字转换为对应的字符 pos++; // 位置后移 注意:递归调用之后,pos已经指向下一个位置,所以不需要额外操作。 在main函数中,调用完convert后,我们在str的当前pos位置添加结束符,即str[pos] = '\0'; 但是,题目中main函数已经写好了,没有添加结束符的语句?我们观察题目中给出的main函数: int main() { int n; char str[MAX_LEN]; scanf("%d", &n); convert(n, str); printf("%s\n", str); return 0; } 所以,在main函数中,在调用convert之后,printf输出str。但是,在convert函数中,我们没有在字符串末尾添加结束符,这会导致输出有问题。 因此,我们必须在convert函数中确保在存储完所有字符后,在字符串末尾添加结束符吗?但是递归函数中我们不知道何时是最后一个字符,所以最好在main函数中手动添加结束符。但是题目不允许修改main函数,所以我们只能在convert函数中处理。 然而,题目要求:不要修改代码框架。也就是说,我们只能填写convert函数。因此,我们需要在convert函数中完成字符串的构建,并在适当位置添加结束符。 但是,递归函数中,我们每次只处理一位数字,那么结束符应该在什么时候添加呢? 考虑:当递归到最深层(即最高位)时,我们处理的是第一位数字,然后回溯时依次处理后面的数字。当处理完最后一位(最低位)时,整个数字转换完成,此时我们需要在整个字符串的末尾添加结束符。 但是,我们无法在递归函数中判断当前位是否是最后一位(因为递归过程中,每一位都是当时的最低位置,但整个数字的最低位是最后处理的)。所以,我们可以这样:在递归函数中,当处理完当前位后,如果当前位是最后一位(即递归已经回溯到最外层),那么我们在后面添加结束符?这很难判断。 另一种思路:在递归函数中,我们只负责转换数字,而结束符由main函数添加。但是题目中的main函数没有添加结束符,所以我们必须修改main函数?但题目不允许修改代码框架。 因此,我们只能改变策略:在递归函数中,当n小于10时,说明是最后一位(但这里注意,最后一位处理完后,后面就应该加结束符?不行,因为递归函数每次处理一位,而整个数字可能有多个位,所以只有整个数字转换完成时,才应该添加结束符。而整个数字转换完成是在最外层的递归函数返回前,但我们不知道当前递归调用是否是最外层。 为了解决这个问题,我们可以利用全局变量pos。当处理完最后一位后,pos就是字符串的长度,此时我们在str[pos]位置添加结束符。但是,我们不知道何时是最后一位。因为递归函数在回溯过程中,当内层递归返回后,外层递归继续执行,直到最外层递归返回。所以,我们可以在最外层的递归函数返回之前添加结束符?但是如何判断最外层? 实际上,我们无法在递归函数内部判断自己是否是最外层的调用。因此,我们可以这样:在convert函数中,当递归处理完高位后,处理当前位,然后判断当前处理完之后,整个数字是否转换完毕?但是整个数字转换完毕的标志是什么?实际上,我们无法直接知道。 重新思考:题目要求不能修改main函数,所以我们必须保证在convert函数调用结束后,str中存储的是一个以'\0'结尾的字符串。因此,我们可以在递归函数中,在每一次存储当前字符后,都认为后面可能还有字符,所以先不添加结束符,直到整个递归结束。但是递归全部结束后,我们怎么添加结束符?因为递归已经全部返回了。 所以,我们可以在递归的最内层(即最高位)开始存储字符,然后依次存储,直到最外层(最低位)存储完毕,此时整个递归调用栈就结束了,然后我们回到main函数。但是,在递归过程中,我们无法在最后一步添加结束符,因为最后一步(最低位)处理完后,返回到上一层(次低位)的递归函数,而次低位处理完后又会返回到上一层,直到最高位处理完后返回。所以,实际上,当递归全部结束,回到main函数时,最后一个字符已经存储了,然后我们在main函数中输出,但是此时字符串没有结束符,所以输出会出错。 因此,我们需要在递归函数中,在最后一次递归调用返回之前,也就是最低位处理完的时候,在最低位字符的后面添加结束符?但最低位处理完后,递归函数就返回了,所以无法在函数返回后添加。 那么,我们可以在递归函数中,当处理完当前位后,判断一下当前位是否是整个数字的最低位?如果是,则添加结束符。但是,整个数字的最低位在递归中是无法直接判断的,因为递归是从高位向低位进行(通过先递归处理高位,再处理低位),当递归到n<10时,说明当前是最高位,然后依次处理后面的位。所以,实际上,递归调用是先处理高位,然后依次处理低位,最后处理的是最低位。而在处理最低位时,递归已经回溯到最外层的函数,此时我们处理的是最低位,但此时整个递归调用栈并没有结束,因为最外层的convert函数还没有返回。所以,我们可以在处理最低位的时候,在存储完最低位字符后,再添加结束符?但是,最低位处理时,并不知道自己是最低位,因为可能有多个位。 因此,我们改变思路:在递归函数中,每次存储字符后,都设置一个结束符在下一个位置?这样不行,因为每次存储后都设置结束符,那么前一次设置的结束符会被下一次覆盖(除了最后一次),所以最后字符串中只有最后一个字符和结束符。 正确的方法:在递归函数中,我们只负责将每一位转换为字符并存储,然后在递归函数返回后,由main函数添加结束符。但是题目不允许修改main函数,所以我们必须另寻他法。 观察题目中的main函数,在调用convert后直接输出str,所以我们必须保证在convert函数结束后,str中已经是一个完整的字符串(以'\0'结尾)。因此,我们可以在convert函数中,在递归开始之前(即最高位处理之前)就预留结束符?不行,因为此时还不知道最高位是什么。 我们可以在递归函数中,当处理完所有位后,在最后一位的后面添加结束符。但是,递归函数中,最后处理的位是最低位,也就是整个数字的最后一位。所以,我们可以在最低位处理完后,立即在它后面添加结束符。但是,如何知道这是最后一位?实际上,在递归函数中,最后一位处理时,n一定是小于10的,并且没有更高位需要递归(即不满足n>=10,所以不会进入递归调用),然后我们存储该位,然后就可以添加结束符了吗?但是,如果这个数只有一位,那么这样是可以的。但是,如果有多位,那么最低位处理时,n<10,但是并不是整个递归的最外层,因为外层还有递归调用(例如,处理123时,先递归处理12,然后处理3,而处理3时,n=3<10,此时处理3,然后返回。但是,处理12的时候,12>=10,所以递归处理1(此时1<10,所以存储'1',然后返回),然后处理2(此时12/10=1已经处理过了,然后处理2,2<10,所以存储'2',然后返回)。然后处理3时,同样存储'3'。所以,在存储3的时候,我们并不知道这是最后一位。 那么,我们可以在递归函数中,当处理完当前位后,判断递归调用返回后,当前递归层是不是最外层?这很难。 因此,我们利用全局变量pos。在递归函数中,我们存储完当前位后,将pos位置设为结束符,但是这样会覆盖之前存储的字符?不行。 一个可行的方案:在递归函数中,当递归调用返回后(即处理完高位后),处理当前低位,然后判断当前递归层是否是最外层?我们可以通过n的和递归深度来间接判断,但是题目没有提供递归深度。 实际上,我们可以在递归函数中,当递归调用返回后,我们处理完当前位,然后判断n是否等于原始的数?不行,因为递归过程中n已经被除过了。 重新考虑:题目要求非负整数,且最大长度为11(包括结束符),所以我们可以等所有字符都存储完后,在最后添加结束符。但是,递归函数中我们不知道何时是最后,所以我们可以不在递归函数中加结束符,而是在递归函数返回后,在main函数中加结束符。但是题目不允许修改main函数。 那么,我们只能修改全局变量pos的用法:在递归函数中,我们存储完所有位后,在最后一位的后面添加结束符。但是,我们不知道什么时候是最后一位。实际上,递归函数中,最后存储的字符就是整个数字的最后一位,存储完最后一位后,递归函数就返回了,所以没有机会添加结束符。 因此,我们可以在递归函数中,在存储字符之前,先判断一下当前递归层是否是最内层(即最高位),如果是,那么我们在存储字符之前,先添加结束符?不行,因为最高位是第一个存储的,后面还有字符。 另一种思路:我们可以在递归函数中,先递归处理高位,然后处理低位,当处理完低位后,在递归返回前,如果当前递归层是最外层(即第一次调用convert的层),则添加结束符。如何判断最外层?我们可以设置一个静态变量来记录递归深度,当深度为0时就是最外层。但是题目不允许修改代码框架(即不能添加额外的全局变量或静态变量)。 因此,我们考虑题目已经给出的全局变量pos。我们可以在递归函数中,在递归调用之前,我们不知道是不是最外层;在递归调用之后,我们处理当前位,然后判断当前n是否小于10,如果是,则说明当前处理的是最高位?不对,因为递归调用之后,当前n是低位的数字,所以当n<10时,说明当前处理的是个位数,但可能不是整个数字的最高位(例如,对于123,处理12时,递归调用convert(12/10, str)即convert(1,str),此时1<10,所以存储'1',然后返回12的处理函数,此时12的处理函数中n=12,然后执行到存储n%10即2,然后返回。所以,在存储2的时候,n=12,大于10,所以不会认为是个位数。所以,在递归函数中,当n<10时,我们存储字符,然后就可以添加结束符吗?不行,因为后面还有低位(对于高位来说,它后面还有低位,所以不能添加结束符)。 所以,我们只能放弃在递归函数内添加结束符。那么,我们只能利用main函数中在调用convert后,我们手动添加结束符。但是题目不允许修改main函数。 因此,我们需要重新审视题目给出的代码框架:main函数中,字符数组str是局部变量,并且没有初始化。在调用convert之前,str是未初始化的。但是,我们可以在convert函数中,在存储完所有字符后,在字符串末尾添加结束符。那么,在什么位置添加结束符?在递归返回前,最后一位存储完后,我们在str[pos]位置添加结束符。但是,最后一位存储完后,pos指向下一个位置(即字符串结尾的下一个位置),所以我们可以在这个位置添加结束符。 但是,由于递归是从内到外返回,最后一位处理完后,返回到上一层递归(即倒数第二位处理函数),而倒数第二位处理函数中,在存储完当前字符后,pos++,然后返回。所以,在最后一位处理函数中,我们存储了最后一位,然后pos++,然后返回。而倒数第二位处理函数中,在递归调用返回后,存储当前位(即倒数第二位),然后pos++,然后返回。所以,整个递归结束后,pos指向字符串末尾的下一个位置(即字符串长度)。因此,我们可以在递归函数的最外层返回前,给str[pos]赋值为'\0'。 但是,问题是如何判断当前递归层是最外层?我们无法判断。所以,我们可以在每次存储完当前字符后,都添加一个结束符,这样最后存储的字符后面的结束符会覆盖前面的结束符,最终只有最后存储的字符后面有结束符。但这样,字符串中只有最后一位,因为每次都被后面的结束符截断了。 所以,我们只能在整个递归结束后,在main函数中添加结束符。但是题目不允许修改main函数,所以我们必须想办法在convert函数中添加结束符,而且不能破坏递归过程。 注意:题目要求递归函数,所以我们必须用递归的方法。 我们可以这样:在递归函数中,当递归到最深时(即n<10),我们存储最高位,此时pos为0(如果之前没有存储的话),然后pos变为1,然后我们在这个位置(str[1])添加结束符?不行,因为最高位后面还有字符。 因此,我们改变策略:在递归函数中,先递归处理高位,然后处理当前位,最后在递归函数返回前,如果是最后一位(即整个数字的最低位),则添加结束符。但是,我们不知道什么时候是最后一位。 实际上,整个递归过程如下(以123为例): convert(123, str) -> 因为123>=10,所以调用convert(12, str) convert(12, str) -> 因为12>=10,所以调用convert(1, str) convert(1, str) -> 因为1<10? 不对,1<10,所以不进行递归调用,直接处理:str[pos] = '1', pos++。然后返回。 回到convert(12, str):存储当前位:str[pos] = 12%10+'0'='2',然后pos++(此时pos=2)。 然后返回。 回到convert(123, str):存储当前位:str[pos]='3',然后pos++(此时pos=3)。 然后返回。 所以,整个递归结束后,pos=3,而字符串是"123",但是str[0]='1',str[1]='2',str[2]='3',而str[3]是未定义的。所以我们需要在str[3]添加结束符。 那么,我们可以在convert函数中,在递归调用返回后(即处理完当前位之后),判断一下当前递归层是不是最外层?如何判断?我们可以用一个静态变量记录递归深度,深度为0时就是最外层。但是题目不允许添加额外的全局变量。 另一种方法:在递归函数中,最外层的调用中,n是原始的数,而内层递归调用中,n是原始数的子部分。所以,我们可以比较n和原始数?但是递归函数中不知道原始数。 因此,我们只能放弃在convert函数内部添加结束符。但是题目要求,我们只能填写convert函数。 重新阅读题目:题目中已经给出了全局变量int pos=0; 而且在main函数中,在调用convert后,直接输出str,所以我们必须保证在convert函数返回时,str中已经是一个完整的字符串(以'\0'结尾)。因此,我们必须在convert函数中,在递归返回前,在字符串末尾添加结束符。那么,我们可以在递归函数的最内层(即最高位处理时)添加结束符吗?不行,因为最高位处理时,后面还有字符。 我们可以在最后一次字符存储后(即最低位存储后)添加结束符。但是,在递归函数中,最低位处理完后,我们返回到上一层递归,而上一层递归中,在存储完当前字符后,我们并不知道后面还有没有字符。 所以,我们可以在convert函数的最后,也就是在即将返回之前,在str[pos]位置添加结束符。注意,每一层递归都会执行到这个添加结束符的语句。但是这样,每一层递归都会在字符串的当前末尾添加结束符,这将导致字符串被截断。 例如,对于123: convert(1,str): 存储'1',pos=1,然后在str[1]添加结束符,所以字符串是"1"。 返回到convert(12,str): 存储'2',此时str[1]='2',覆盖了之前的结束符,字符串变成"12",然后pos=2,然后在str[2]添加结束符,所以字符串变成"12"。 然后返回到convert(123,str): 存储'3',覆盖了str[2]的结束符,字符串变成"123",然后pos=3,在str[3]添加结束符,所以最后字符串是"123"。 这样,虽然每一层都添加了结束符,但是内层添加的结束符会被外层覆盖,所以最终只有最外层添加的结束符保留了下来。因此,这个方法是可行的。 所以,我们在convert函数的最后,添加: str[pos] = '\0'; 但是,这样会导致pos++了吗?没有,我们添加结束符后,pos并没有移动,因为结束符不需要移动pos(下一次存储会覆盖它,而且我们添加结束符后,pos应该指向下一个位置,以便于外层添加字符时覆盖这个结束符?但是,外层会先存储字符,然后再添加结束符,所以内层添加的结束符会被外层覆盖。 具体过程: convert(1,str): n=1, 因为n<10? 不满足n>=10,所以不递归,然后执行:str[0] = '1', pos=1。 然后执行:str[1] = '\0'; // 此时字符串为"1\0" 返回。 convert(12,str): 先递归调用convert(1,str) -> 已经存储了'1',并添加了结束符在str[1]。 返回后,执行:str[1] = 12%10+'0' = '2',覆盖了之前的结束符,然后pos=2。 然后执行:str[2] = '\0'; // 此时字符串为"12\0" 返回。 convert(123,str): 先递归调用convert(12,str) -> 得到"12\0" 然后执行:str[2] = 123%10+'0'='3',覆盖了str[2]的结束符,然后pos=3。 然后执行:str[3]='\0'; // 此时字符串为"123\0" 返回。 所以,最后字符串是"123\0",正确。 但是,注意:在存储字符和添加结束符之间,字符串中间可能会出现结束符,但是马上就会被覆盖,所以最终只有最后一个结束符保留。 因此,我们在convert函数中,在递归调用之后,存储当前位字符,然后pos++,然后在str[pos]位置添加结束符。 但是,这样会多一次添加结束符,而且每次添加结束符都是在存储完当前字符后的下一个位置。 所以,convert函数代码如下: void convert(int n, char str[]) { if (n >= 10) { convert(n/10, str); } // 将当前位转换为字符 str[pos] = n % 10 + '0'; pos++; // 在下一个位置添加结束符 str[pos] = '\0'; } 但是,这样写,对于n=0的情况,会出错吗?我们来看: n=0: 0<10,所以不递归。 str[0] = 0+'0' = '0' pos变为1 str[1] = '\0' 所以字符串是"0",正确。 但是,注意,全局变量pos在每次程序运行时只初始化一次。如果程序多次调用convert(但题目中main函数只调用一次),所以没问题。 但是,如果输入0,那么递归函数不会递归,直接存储'0'和结束符。 所以,这个方案可行。 但是,我们还要注意:题目要求非负整数,所以n>=0。 因此,我们按照这个思路编写convert函数。 注意:题目要求不要修改代码框架,所以我们就填写这个函数体。 因此,完整的convert函数如下: */ </think> 下面是根据需求编写的递归函数`convert`实现,以及完整的程序代码: ```c #include <stdio.h> #define MAX_LEN 11 int pos = 0; // 全局变量记录当前存储位置 void convert(int n, char str[]) { // 递归处理高位数字 if (n >= 10) { convert(n / 10, str); } // 将当前位数字转换为字符 str[pos] = n % 10 + '0'; pos++; // 添加字符串结束符 str[pos] = '\0'; } int main() { int n; char str[MAX_LEN]; scanf("%d", &n); convert(n, str); printf("%s\n", str); return 0; } ``` ### 代码说明: 1. **递归逻辑**: - 当 `n >= 10` 时,先递归处理高位数字(`n/10`) - 递归返回后,将当前最低位数字 `n%10` 转换为字符 - 通过 `n % 10 + '0'` 将数字转换为ASCII字符(如 `5 → '5'`) - 每次转换后更新全局变量 `pos` 记录存储位置 2. **字符串结束符**: - 每次添加新字符后,立即在下一位置设置结束符 `\0` - 确保字符串始终有效,可被 `printf` 正确输出 3. **非负整数处理**: - 题目要求非负整数,故无需处理负数情况 - 输入0时,输出字符串 `"0"` ### 示例输入/输出: | 输入 | 输出 | |------|------| | `1234` | `"1234"` | | `0` | `"0"` | | `98765`| `"98765"` | ### 注意事项: 1. 全局变量 `pos` 必须在每次程序运行时初始化(已通过 `int pos=0` 实现) 2. 字符数组长度 `MAX_LEN=11` 可处理最大10位整数(含结束符) 3. 递归深度与数字位数相同(如 `2147483647` 递归10次) ### 算法复杂度: - **时间复杂度**:$O(d)$(d为数字位数) - **空间复杂度**:$O(d)$(递归栈空间) [^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值