C 字符串数组和指针传递方法不起作用

Array of c-strings and pass by pointer method does not work

使用指针传递方法为 C 字符串数组分配新值无法正常工作。

在"LettersToCapital"方法内部,新值被正确地分配给C字符串数组,但是,一旦在方法之外读取C字符串数组内容,结果都是错误的。该函数应该做的就是将所有小写字母大写。我肯定做错了什么,但那会是什么?

如果在第 53 行,变量 tempStr 被替换为常量字符串文字,例如"aqua" 那么值在函数外保持不变。但是直接从 char 数组 (tempStr) 分配给 char 数组指针 (*(string +i)) 不会产生正确的结果。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void LettersToCapital(char **string, int size);
void ReadOutAOC(char **string, int size);

int main()
{
    char *canadianProvinces[] = {"British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec", "New Brunswick", "Nova Scotia", "Prince Edward Island", "Newfoundland", "Yukon", "Northwest Territories", "Nunavut"};

    int numOfCanProv = sizeof(canadianProvinces) / sizeof(int);

    printf("\nNumber of Canadian provinces %d\n", numOfCanProv);

    // printing all provinces before conversion
    printf("\nBefore \"all to capital conversion\"\n\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);    

    LettersToCapital(canadianProvinces, numOfCanProv);
    // Temp(canadianProvinces);

    // printing all provinces after conversion
    printf("\nAfter \"all to capital conversion\"\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);
}

void ReadOutAOC(char **string, int size)
{
    printf("\n");

    for(int i = 0; i < size; i++)
        printf("String outside the assignment method[%d]: %s\n", i + 1, *(string + i));
}

void LettersToCapital(char **string, int size)
{
    char tempStr[256];

    for(int i = 0; i < size; i++)
    {
        for(int j = 0; j < strlen(*(string + i)); j++)
        {
            if(*(*(string + i) + j) > 96 && *(*(string + i) + j) < 123)
                tempStr[j] = *(*(string + i) + j) - 32;
            else
                tempStr[j] = *(*(string + i) + j);
        }
        tempStr[strlen(*(string + i))] = '[=10=]';
        *(string + i) = tempStr; // does not work
        //*(string + i) = "aqua"; // try this instead
        printf("String inside the assignment method[%d]: %s\n", i + 1, *(string + i));
    }
}

预期输出应为:

之前"all to capital conversion"

不列颠哥伦比亚省

阿尔伯塔

萨斯喀彻温省

...

"all to capital conversion"

之后

不列颠哥伦比亚省

艾伯塔省

萨斯喀彻温省

...

以下代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 注意访问数据字符串的正确方法
  4. 注意头文件的正确用法:ctype.h和函数:toupper()
  5. 未使用的变量,如 char tempStr[256]; 和对该变量的所有引用都被删除
  6. 将由于尝试修改只读内存中的数据而导致 'seg fault' 事件

现在是错误代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

void LettersToCapital(char **string, size_t size);
void ReadOutAOC(char **string, size_t size);

int main( void )
{
    char *canadianProvinces[] = 
    {
        "British Columbia", 
        "Alberta", 
        "Saskatchewan", 
        "Manitoba", 
        "Ontario", 
        "Quebec", 
        "New Brunswick", 
        "Nova Scotia", 
        "Prince Edward Island", 
        "Newfoundland", 
        "Yukon", 
        "Northwest Territories", 
        "Nunavut"
    };

    size_t numOfCanProv = sizeof(canadianProvinces) / sizeof(canadianProvinces[0]);

    printf("\nNumber of Canadian provinces %lu\n", numOfCanProv);

    // printing all provinces before conversion
    printf("\nBefore \"all to capital conversion\"\n\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);    

    LettersToCapital(canadianProvinces, numOfCanProv);
    // Temp(canadianProvinces);

    // printing all provinces after conversion
    printf("\nAfter \"all to capital conversion\"\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);
}

void ReadOutAOC(char **string, size_t size)
{
    printf("\n");

    for( size_t i = 0; i < size; i++)
        printf("String outside the assignment method[%lu]: %s\n", i + 1, string[i] );
}

void LettersToCapital(char **string, size_t size)
{
    for( size_t i = 0; i < size; i++)
    {
        size_t j;
        for( j = 0; j < strlen( string[i] ); j++)
        {
            string[i][j] = (char)toupper( string[i][j] );
        }

        printf("String inside the assignment method[%lu]: %s\n", i + 1, string[i] );
    }
}

运行 以上代码产生以下输出:

Before "all to capital conversion"


String outside the assignment method[1]: British Columbia
String outside the assignment method[2]: Alberta
String outside the assignment method[3]: Saskatchewan
String outside the assignment method[4]: Manitoba
String outside the assignment method[5]: Ontario
String outside the assignment method[6]: Quebec
String outside the assignment method[7]: New Brunswick
String outside the assignment method[8]: Nova Scotia
String outside the assignment method[9]: Prince Edward Island
String outside the assignment method[10]: Newfoundland
String outside the assignment method[11]: Yukon
String outside the assignment method[12]: Northwest Territories
String outside the assignment method[13]: Nunavut
Segmentation fault (core dumped)

使用 gdb 单步执行程序显示段错误事件的原因是这一行:

string[i][j] = (char)toupper( string[i][j] );

因为它正在尝试更改只读内存中的 value/byte/char

建议修改数据的定义为:

#define MAX_PROV_NAME_LEN 50
char canadianProvinces[][ MAX_PROV_NAME_LEN ] = 
    {
        "British Columbia", 
        "Alberta", 
        "Saskatchewan", 
        "Manitoba", 
        "Ontario", 
        "Quebec", 
        "New Brunswick", 
        "Nova Scotia", 
        "Prince Edward Island", 
        "Newfoundland", 
        "Yukon", 
        "Northwest Territories", 
        "Nunavut"
    };

然后调整其余代码以匹配即可解决问题。

更正后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_PROV_NAME_LEN 50

void LettersToCapital(char string[][ MAX_PROV_NAME_LEN ], size_t size);
void ReadOutAOC(char string[][ MAX_PROV_NAME_LEN ], size_t size);

int main( void )
{
    char canadianProvinces[][ MAX_PROV_NAME_LEN ] = 
    {
        "British Columbia", 
        "Alberta", 
        "Saskatchewan", 
        "Manitoba", 
        "Ontario", 
        "Quebec", 
        "New Brunswick", 
        "Nova Scotia", 
        "Prince Edward Island", 
        "Newfoundland", 
        "Yukon", 
        "Northwest Territories", 
        "Nunavut"
    };

    size_t numOfCanProv = sizeof(canadianProvinces) / sizeof(canadianProvinces[0]);

    printf("\nNumber of Canadian provinces %lu\n", numOfCanProv);

    // printing all provinces before conversion
    printf("\nBefore \"all to capital conversion\"\n\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);    

    LettersToCapital(canadianProvinces, numOfCanProv);
    // Temp(canadianProvinces);

    // printing all provinces after conversion
    printf("\nAfter \"all to capital conversion\"\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);
}

void ReadOutAOC(char string[][ MAX_PROV_NAME_LEN ], size_t size)
{
    printf("\n");

    for( size_t i = 0; i < size; i++)
        printf("String outside the assignment method[%lu]: %s\n", i + 1, string[i] );
}

void LettersToCapital(char string[][ MAX_PROV_NAME_LEN ], size_t size)
{
    for( size_t i = 0; i < size; i++)
    {
        size_t j;
        for( j = 0; j < strlen( string[i] ); j++)
        {
            string[i][j] = (char)toupper( string[i][j] );
        }

        printf("String inside the assignment method[%lu]: %s\n", i + 1, string[i] );
    }
}

a 运行 更正后的代码导致以下输出:

Number of Canadian provinces 13

Before "all to capital conversion"


String outside the assignment method[1]: British Columbia
String outside the assignment method[2]: Alberta
String outside the assignment method[3]: Saskatchewan
String outside the assignment method[4]: Manitoba
String outside the assignment method[5]: Ontario
String outside the assignment method[6]: Quebec
String outside the assignment method[7]: New Brunswick
String outside the assignment method[8]: Nova Scotia
String outside the assignment method[9]: Prince Edward Island
String outside the assignment method[10]: Newfoundland
String outside the assignment method[11]: Yukon
String outside the assignment method[12]: Northwest Territories
String outside the assignment method[13]: Nunavut
String inside the assignment method[1]: BRITISH COLUMBIA
String inside the assignment method[2]: ALBERTA
String inside the assignment method[3]: SASKATCHEWAN
String inside the assignment method[4]: MANITOBA
String inside the assignment method[5]: ONTARIO
String inside the assignment method[6]: QUEBEC
String inside the assignment method[7]: NEW BRUNSWICK
String inside the assignment method[8]: NOVA SCOTIA
String inside the assignment method[9]: PRINCE EDWARD ISLAND
String inside the assignment method[10]: NEWFOUNDLAND
String inside the assignment method[11]: YUKON
String inside the assignment method[12]: NORTHWEST TERRITORIES
String inside the assignment method[13]: NUNAVUT

After "all to capital conversion"

String outside the assignment method[1]: BRITISH COLUMBIA
String outside the assignment method[2]: ALBERTA
String outside the assignment method[3]: SASKATCHEWAN
String outside the assignment method[4]: MANITOBA
String outside the assignment method[5]: ONTARIO
String outside the assignment method[6]: QUEBEC
String outside the assignment method[7]: NEW BRUNSWICK
String outside the assignment method[8]: NOVA SCOTIA
String outside the assignment method[9]: PRINCE EDWARD ISLAND
String outside the assignment method[10]: NEWFOUNDLAND
String outside the assignment method[11]: YUKON
String outside the assignment method[12]: NORTHWEST TERRITORIES
String outside the assignment method[13]: NUNAVUT