【问题标题】:Mystery of the mysterious P神秘P之谜
【发布时间】:2017-05-23 13:48:22
【问题描述】:

背景:

我正在尝试创建一个程序,该程序采用用户名(假设输入是干净的),并打印出名称的首字母。

目标:

  • 尝试使用 CS50 进行 C 编程
  • 熟悉 malloc 和 realloc

代码:

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

string prompt(void);
char *getInitials(string input);
char *appendArray(char *output,char c,int count);

//Tracks # of initials
int counter = 0;

int main(void){

    string input = prompt();
    char *output = getInitials(input);
    for(int i = 0; i < counter ; i++){

        printf("%c",toupper(output[i]));
    }


}

string prompt(void){

    string input;

    do{
        printf("Please enter your name: ");
        input = get_string();
    }while(input == NULL);

    return input;

}

char *getInitials(string input){

    bool initials = true;
    char *output;
    output = malloc(sizeof(char) * counter);
    for(int i = 0, n = strlen(input); i < n ; i++){
        //32 -> ASCII code for spacebar
        //9  -> ASCII code for tab
        if(input[i] == 32 || input[i] == 9 ){
            //Next char after spaces/tab will be initial
            initials = true;


        }else{//Not space/tab
            if(initials == true){
                counter++;
                output = appendArray(output,input[i],counter);

                initials = false;


            }        

        }
        // eprintf("Input[i] is : %c\n",input[i]);
        // eprintf("Counter is : %i\n",counter);
        // eprintf("i is : %i\n",i);
        // eprintf("n is : %i\n",n);



    }

    return output;
}

char *appendArray(char *output,char c,int count){

    // allocate an array of some initial (fairly small) size;
    // read into this array, keeping track of how many elements you've read;
    // once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents;
    // repeat until done.


    //pointer to memory
    char *data = malloc(0);
    //Increase array size by 1
    data = realloc(output,sizeof(char) * count);
    //append the latest initial
    strcat(data,&c);
    printf("Value of c is :%c\n",c);
    printf("Value of &c is :%s\n",&c);
    for(int i = 0; i< count ; i++){
        printf("Output: %c\n",data[i]);
    }
    return data;

}

问题:

输出不是我所期望的,因为输出中出现了一个神秘的 P。

例如,当我输入巴拉克奥巴马这个名字时,我得到的不是结果:BO,而是结果 BP,无论我选择输入什么名字都会发生同样的情况,最后一个首字母始终是 P。

输出:

Please enter your name: Barack Obama
Value of c is :B
Value of &c is :BP
Output: B
Value of c is :O
Value of &c is :OP
Output: B
Output: P
BP

我做了什么:

我已经将问题追溯到 appendArray 函数,更具体地说是 &c(c 的地址)的值,尽管我不知道是什么导致了 P 出现,它意味着什么,它为什么出现以及我怎么能摆脱它。

无论我什么时候输入,P的值都会显示出来。

我们将不胜感激有关它发生的原因以及我能做些什么来解决它的见解。

谢谢!

【问题讨论】:

  • 有一个cs50 stack exchange site,如果你有兴趣的话。
  • @pmg 我不知道。感谢您指出!
  • output = malloc(sizeof(char) * counter); --> output = malloc(++counter); *output = 0;
  • strcat(data,&amp;c); printf("Value of c is :%c\n",c); printf("Value of &amp;c is :%s\n",&amp;c); --> strncat(data, &amp;c, 1);
  • for(int i = 0; i &lt; counter ; i++){ --> for(int i = 0; output[i] ; i++){

标签: c malloc realloc cs50


【解决方案1】:

几个问题,按重要性降序排列...

第一个问题 - appendArray 中的 c 不是字符串 - 它不是以 0 结尾的字符值的序列c 是单个char 对象,存储单个char 值。

当您尝试将c打印为字符串时,如

printf("Value of &c is :%s\n",&c);

printf 写出从c 地址开始的字符值的序列,直到它看到一个0 值字节。 无论出于何种原因,紧跟在c 之后的字节包含值 80,它是字符 'P' 的 ASCII(或 UTF-8)代码。下一个字节包含一个 0(或者有一个包含不可打印字符的字节序列,后跟一个 0 值字节)。

同样,使用&amp;c 作为strcat 的参数是不合适的,因为c 不是字符串。相反,您应该这样做

data[count-1] = c;

其次,如果要将data 数组视为字符串,则必须确保将其大小至少比首字母的数量大1,并将0 写入最终元素:

data[count-1] = 0; // after all initials have been stored to data

第三,

char *data = malloc(0);

没有任何作用,行为是实现定义的,您立即通过调用realloc覆盖malloc(0)的结果:

data = realloc(output,sizeof(char) * count);

所以,完全摆脱malloc(0) 调用;要么将 data 初始化为 NULL,要么使用 realloc 调用对其进行初始化:

char *data = realloc( output, sizeof(char) * count );

第四,避免使用“幻数”——意义超出其直接字面值的数字常量。当您想与 character 值进行比较时,请使用 character 常量。 IOW,改变

if(input[i] == 32 || input[i] == 9 ){

if ( input[i] == ' ' || input[i] == '\t' )

这样您就不必担心字符编码是 ASCII、UTF-8、EBCDIC 还是其他系统。 ' ' 表示 空格 无处不在,'\t' 表示 tab 无处不在。

终于……

我知道您进行此练习的部分动机是熟悉 mallocrealloc,但我想提醒您一些注意事项:

realloc 可能是一项代价高昂的操作,它可能将数据移动到新位置,并且可能失败。你真的不想realloc 一次缓冲一个字节。相反,最好以块的形式realloc。一个典型的策略是将当前缓冲区大小乘以 > 1 的某个因子(通常加倍):

char *tmp = realloc( data, current_size * 2 );
if ( tmp )
{
  current_size *= 2;
  data = tmp;
}

您应该始终检查malloccallocrealloc 调用的结果,以确保它在尝试访问该内存之前成功。

小风格注释:

尽可能避免使用全局变量。没有理由 counter 应该是全局的,特别是因为您将它作为参数传递给 appendArray。将其声明为 main 本地并将其作为参数(通过引用)传递给 getInput

int main( void )
{
  int counter = 0;
  ...
  char *output = getInitials( input, &counter );
  for(int i = 0; i < counter ; i++)
  {
    printf("%c",toupper(output[i]));
  } 
  ...
}

/**
 * The "string" typedef is an abomination that *will* lead you astray,
 * and I want to have words with whoever created the CS50 header.
 *
 * They're trying to abstract away the concept of a "string" in C, but 
 * they've done it in such a way that the abstraction is "leaky" - 
 * in order to use and access the input object correctly, you *need to know*
 * the representation behind the typedef, which in this case is `char *`.
 *
 * Secondly, not every `char *` object points to the beginning of a 
 * *string*.    
 *
 * Hiding pointer types behind typedefs is almost always bad juju.  
 */
char *getInitials( const char *input, int *counter ) 
{
   ...
   (*counter)++;                                   // parens are necessary here
   output = appendArray(output,input[i],*counter); // need leading * here
   ...
}

【讨论】:

  • 名副其实的“教育”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 2018-11-28
  • 2013-03-11
  • 2014-06-05
  • 2013-08-13
  • 2010-11-12
相关资源
最近更新 更多