【问题标题】:Array of c-strings and pass by pointer method does not workc字符串数组和指针传递方法不起作用
【发布时间】:2019-08-08 17:18:48
【问题描述】:

使用指针传递方法为 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))] = '\0';
        *(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));
    }
}

预期的输出应该是:

在“全部转为资本”之前

不列颠哥伦比亚省

阿尔伯塔

萨斯喀彻温省

...

“全部转为大写”后

不列颠哥伦比亚省

阿尔伯塔

萨斯喀彻温省

...

【问题讨论】:

  • sizeof(canadianProvinces) / sizeof(int) 看起来很可疑。
  • *(string + i) = tempStr; 在这里,您将指针指定为指向具有自动存储持续时间的对象。一旦你离开这个函数,这些数据就是垃圾。此外,在您离开函数之前,您正在循环中覆盖该数据。
  • 对于 any 指针或数组a 和索引i,表达式*(a + i)恰好等于a[i]。现在在所有指针算法的上下文中考虑这一点,它可以变得更简单(也更少编写)。
  • 并且不要使用像32123 这样的幻数。如果要检查字符是否为大写,请使用isupper。并使用toupper 进行转换(即使字符已经是大写,您也可以调用它)。
  • 关于:char *canadianProvinces[] = { ... }; 这会生成一个字符指针数组,指向只读内存中的字符串。此类字符串无法修改。尝试修改此类字符串会导致段错误事件

标签: c arrays pass-by-reference c-strings


【解决方案1】:

以下代码:

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

现在是错误代码:

#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单步执行程序显示seg fault事件的原因是这一行:

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

因为它试图更改只读内存中的值/字节/字符

建议修改数据的定义为:

#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] );
    }
}

运行更正后的代码会产生以下输出:

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

【讨论】:

  • 谢谢你,你对我真的很友善和乐于助人,如果我们互相吃肉,我会喝酒。但是,我很失望地发现或更诚实地“再次提醒”char *string[] 是一个指向只读内存中字符的指针数组。
  • 如果这回答了你的问题,那么请选择答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多