【问题标题】:Storing tokens from 1D char array to char** array将标记从一维 char 数组存储到 char** 数组
【发布时间】:2021-12-27 18:36:46
【问题描述】:

我正在尝试编写一个程序,该程序将动态分配足够的空间来将所有单词存储在一个由空格分隔的一维字符数组中。 例如:

char *literal = "The quick brown fox";
char **words = { "The", "quick", "brown", "fox" };

我写的程序在尝试strncpy(str[buff_ptr],tok,strlen(tok));时不断出现段错误

我将在下面发布我的代码:

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

char *mutableString(char *lit) {
    int size = strlen(lit);
    char *str = (char *)malloc(sizeof(char) * size);
    strncpy(str, lit, size + 1);
    return str;
}

int numTokens(char *str, const char *DELIM) {
    char* clone = (char*)malloc(sizeof(char*));
    strncpy(clone, str, strlen(str) + 1);
    int count = 0;
    for (char *tok = strtok(clone, " "); tok != NULL; tok = strtok(NULL, " "))
        count++;
    free(clone);
    return count;
}

char **tokenize(char *str, const char *DELIM) {
    printf("tokenize-------------------------\n");
    int size = numTokens(str, DELIM);
    //allocate space on heap for buffer
    char **buff = (char **)malloc(size * sizeof(char *));
    //get first word
    char *tok = strtok(str, DELIM);
    int buff_ptr = 0;
    while (tok != NULL) {
        strncpy(buff[buff_ptr], tok, strlen(tok) + 1);
        printf("buff[%d]%s\n", buff_ptr, buff[buff_ptr]);
        //increment to next word for storage
        buff_ptr++;
        //find next word in string
        tok = strtok(NULL, DELIM);
    }
    for (int i = 0; i < size; i++) {
        printf("%s\n", buff[i]);
    }
    //return 2D pointer
    return buff;
}

int main() {
    char *literal = "some literal string.";
    //convert string to mutable string for strtok
    char *str = mutableString(literal);
    //set 2D pointer equal to the pointer address returned
    char **no_spaces_str = tokenize(str, " ");
    printf("%s\n", str);
    for (int i = 0; i < numTokens(str, " "); i++) {
        printf("%s\n", no_spaces_str[i]);
    }
    //free heap allocated memory
    free(str);
    free(no_spaces_str);
    return 0;
}

lldb栈变量见附件:

【问题讨论】:

  • strlen 给出chars 的数量,您需要分配+1 的大小来满足空终止\0
  • 这是有道理的@TruthSeeker,但为什么会出现段错误,它不会将垃圾值读取到空终止符吗?
  • @KyleC。当您将 NUL 终止符写入您不拥有的内存时,您可能会出现段错误。当您读取未 NUL 终止的内存时,您可能会在获得 NUL 之前遇到不允许读取的内存
  • strncpy(buff[buff_ptr],tok,strlen(tok)); 行导致seg-fault 因为在将单词复制到它之前没有分配任何内存
  • 如果您负担得起,只需将 NUL 添加到您现有的字符串中并创建一个指向段的指针缓冲区。确保你只释放第一个。

标签: c split dynamic-memory-allocation c-strings strncpy


【解决方案1】:

在函数 mutableString 中动态分配了不包含字符串的字符数组str

char* mutableString(char* lit){
  int size = strlen(lit);
  char* str = (char*)malloc(sizeof(char)*size);
  strncpy(str,lit,size);
  return str;
}

所以其他函数调用未定义的行为,例如在这个 for 循环中

int numTokens(char* str, const char* DELIM){
  int count = 0;
  for(; *str != '\0'; str++)
  //...

此外,如果数组包含一个字符串,那么函数numTokens 是不正确的,因为例如当传递的字符串只包含一个单词时,它会返回 0。

也在tokenize函数中

strncpy(buff[buff_ptr],tok,strlen(tok));

有使用未初始化的指针buff[buff_ptr] 分配类似。

char **buff = (char**)malloc(size*sizeof(char*));

您再次尝试复制字符串而不包括终止零字符 '\0;使用 eth 函数strncpy

所以这个调用在 main 中

printf("%s\n",no_spaces_str[i]);

也会调用未定义的行为。

【讨论】:

  • mutableString 怎么不包含字符串?我将文字字符串复制到分配的内存中以使其可变。是的,我知道 numTokens 不是查找单词的充分方法,但在这种情况下,我通过在文字字符串的末尾添加一个空格来强制它正确。我的主要问题是如何使用动态分配的内存将令牌从一维字符数组复制到二维字符数组?你能解释一下你的意思是“未初始化的指针buff [buff_ptr]分配像”吗?谢谢
  • @KyleC 字符串是包括终止零字符'\0'的字符序列。而且您在制作副本时没有包括它。所以 for 循环中的这个条件 *str != '\0' 会调用未定义的行为。
  • 啊,所以当你 strncpy 时,即使知道文字字符串已经有一个空终止符,strlen 不计算空终止符,所以我需要显式地 +1 到我的大小?
  • @KyleC 是的,您需要在长度上再增加一个字节并使用 memcpy 或 strcpy。
  • 感谢您的建议我进行了必要的更改以将空终止符添加到我的 char 数组中,我更新了我的 numTokens 以计算所有令牌,但我仍然存在段错误。我进行了更新以显示我的编辑。如果您有任何其他建议,请告诉我,谢谢。
【解决方案2】:

这是上面代码的修正版

  1. 当你复制字符串时,你应该为 '\0' 添加 1 个字符

    int size = strlen(lit)+1;

  2. 令牌缓冲区大小应为 size+1

    int size = numTokens(str, DELIM)+1;

  3. 不需要Strncpy strncpy(buff[buff_ptr], tok, strlen(tok) + 1); 你已经复制了字符串char* str = mutableString(literal); 只需指向每个下一个标记的第 n 个缓冲区 buff[buff_ptr]=tok;

  4. for (int i = 0; i<numTokens(str, " "); i++){
        printf("%s\n", no_spaces_str[i]);
    }
    

    此代码无法正常工作。 strtok 操作您传入的字符串并返回指向它的指针,因此不分配内存。 所以所有的空格都会被 '\0' 代替

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

#pragma warning(push)
#pragma warning(disable : 4996)

char* mutableString(char* lit){
    int size = strlen(lit)+1;
    char* str = (char*)malloc(sizeof(char)*size);
    strncpy(str, lit, size);
    return str;
}
int numTokens(char* str, const char* DELIM){
    int count = 0;
    for (; *str != '\0'; str++)
    {
        if (*str == ' ')
            count++;
    }
    return count;
}
char** tokenize(char* str, const char* DELIM){
    printf("tokenize-------------------------\n");
    int size = numTokens(str, DELIM)+1;
    //allocate space on heap for buffer
    char **buff = (char**)malloc((size)*sizeof(char*));
    //get first word
    char* tok = strtok(str, DELIM);
    int buff_ptr = 0;
    while (tok != NULL){

        buff[buff_ptr]=tok;
        printf("buff[%d]%s\n", buff_ptr, buff[buff_ptr]);
        //increment to next word for storage
        buff_ptr++;
        //find next word in string
        tok = strtok(NULL, DELIM);
    }

    for (int i = 0; i<size; i++){
        printf("%s\n", buff[i]);
    }
    //return 2D pointer
    return buff;
}
int main(){
    char* literal = "some literal string.";
    //convert string to mutatable string for strtok
    char* str = mutableString(literal);
    //set 2D pointer equal to the pointer addres returned
    char** no_spaces_str = tokenize(str, " ");
    printf("%s\n", str);

    for (int i = 0; i<numTokens(str, " "); i++){
        printf("%s\n", no_spaces_str[i]);
    }
    //free heap allocated memory
    free(str);

    free(no_spaces_str);
    return 0;
}

结果

tokenize-------------------------
buff[0]some
buff[1]literal
buff[2]string.
some
literal
string.
some

【讨论】:

  • refer。代码无法产生有效的输出
  • @TruthSeeker 我修复了这个问题,现在代码返回正确的输出
  • refer程序返回正确结果
【解决方案3】:
char* mutableString(char* lit){
  int size = strlen(lit)+1;
  char* str = (char*)malloc(sizeof(char)*size);
  strncpy(str,lit,size);
  return str;
}
int numTokens(char* str, const char* DELIM){
  int size = strlen(str)+1;
  char* clone = (char*)malloc(sizeof(char)*size);
  strncpy(clone,str,size);
  int count = 0;
  for(char* tok = strtok(clone," "); tok != NULL; tok=strtok(NULL, " "))
      count++;
  free(clone);
  return count;
}
char** tokenize(char* str, const char* DELIM){
  int size = strlen(str)+1;
  char* clone = (char*)malloc(sizeof(char)*size);
  strncpy(clone,str,size);
  // printf("tokenize-------------------------\n");
  int size = numTokens(str, DELIM);
  //allocate space on heap for buffer
  char **buff = (char**)calloc(size,sizeof(char*));
  //get first word
  char* tok = strtok(clone,DELIM);
  int buff_ptr = 0;
  while(tok != NULL){
    // printf("token%d:%s\n",buff_ptr,tok);
    buff[buff_ptr] = (char*)malloc(sizeof(char)*strlen(tok)+1);
    strncpy(buff[buff_ptr],tok,strlen(tok)+1);
    //increment to next word for storage
    buff_ptr++;
    //find next word in string
    tok = strtok(NULL, DELIM);
  }
  //return 2D pointer
  free(clone);
  return buff;
}
int main(){
  char* literal = "some literal string.";
  //convert string to mutatable string for strtok
  char* str = mutableString(literal);
  //set 2D pointer equal to the pointer addres returned
  char** no_spaces_str = tokenize(str, " ");
  int num_words = numTokens(str," ");
  char* oneD = (char*)calloc(strlen(str)+1,sizeof(char));
  for(int i = 0;i<num_words;i++){
    strncat(oneD,no_spaces_str[i],strlen(no_spaces_str[i])+1);
    printf("%s\n",oneD);
  }
  
  //free heap allocated memory
  free(str);
  free(no_spaces_str);
  free(oneD);
  return 0;
}

是我的问题的解决方案。感谢所有评论并帮助我更好地理解动态内存的人。

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
  • 你的程序会再次崩溃
  • 您的解决方案在写入未分配内存时遇到问题。 char* str = (char*)malloc(sizeof(char)*size); 这里分配的内存比复制的strncpy(str,lit,size+1)小1
  • 感谢您指出这一点。
【解决方案4】:

除了@Vlad from Moscow提到的几点,

malloc返回值不能是type-castedDo I cast the result of malloc?

我试图清理代码找到下面的sn-p,DEMO

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

typedef struct{
    char** buff;
    int    size;
}Array_2d;

char* mutableString(const char* lit){
  int size = strlen(lit);
  char* str = malloc(size);
  strncpy(str,lit,size+1);
  return str;
}

int getNextWordLength(const char* str){
  int index = 0;
  while(*str && (*str != ' ')){
      //printf("%c",*str);
      ++index;
      ++str;
  }
  return index;
}

int numTokens(const char* str){
  int count = 0;
  for(; *str != '\0'; str++)
  {
    if(*str == ' ')
      count++;
  }
  return count;
}

void tokenize(const char* str, const char *DELIM, Array_2d *array){

  int len = strlen(str)+1;

    if(!str && !len){
        array->buff = 0;
        array->size = 0;
    }

    int number_of_words = numTokens(str)+1;
    //allocate space on heap for buffer
    char **buff = (char**)malloc(number_of_words*sizeof(char*));

    int index = 0; 

    do{
        //get first word
        
        int word_length = getNextWordLength(str);
        
        //To compensate null terminal
        buff[index] = malloc(word_length+1);

        strncpy(buff[index], str,word_length);

        buff[index][word_length+1] = '\0';

        str += word_length+1;
        ++index;

    }while(index < number_of_words);

    //update return value
    array->buff = buff;
    array->size = number_of_words;

}

int main(){
    char* literal = "hello world this is test";
    //convert string to mutatable string for strtok
    char* str = mutableString(literal);
    printf("Complete String is : %s\n",str);
    

    Array_2d array;
    // set 2D pointer equal to the pointer addres returned
     tokenize(str, " ",&array);

    printf("Tokenized String\n");
    for(int i=0;i<array.size;i++){
        printf("%s\n",array.buff[i]);
    }

    free(str);

    for(int i =0;i< array.size; ++i)
    free(array.buff[i]);

    free(array.buff);

  return 0;
}

【讨论】:

  • 感谢@TruthSeeker 看起来您几乎完全重写了我的代码。我的主要问题是了解动态内存分配,我想我现在明白了,但是我什至没有考虑过很好的结构解决方案
  • 你提到不投也很有趣。我对 malloc 的理解返回一个 void* 指针,因此指定要返回的 ptr 类型告诉编译器将在该地址取消引用什么值类型。我知道较新版本的 C 编译器会为您执行此操作,但就像 return 0 是不必要的一样,我认为为了清晰起见,它仍然是一种很好的做法。
猜你喜欢
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 2021-06-14
相关资源
最近更新 更多