背景:
我正在尝试创建一个程序,该程序采用用户名(假设输入是干净的),并打印出名称的首字母。
目的:
- 尝试使用 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 : %cn",input[i]);
// eprintf("Counter is : %in",counter);
// eprintf("i is : %in",i);
// eprintf("n is : %in",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 :%cn",c);
printf("Value of &c is :%sn",&c);
for(int i = 0; i< count ; i++){
printf("Output: %cn",data[i]);
}
return data;
}
问题:
输出不是我预期的,因为输出中出现了一个神秘的 P。
例如,当我输入巴拉克·奥巴马这个名字时,我得到的结果不是 result: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 的值都会显示。
关于它为什么会发生以及我能做些什么来解决它的见解将不胜感激。
谢谢!
几个问题,按重要性降序排列...
第一个问题 -appendArray
中的c
不是字符串- 它不是以 0 结尾的字符值序列。c
是单个char
对象,存储单个char
值。
当您尝试将c
打印为字符串时,如
printf("Value of &c is :%sn",&c);
printf
写出从c
地址开始的字符值序列,直到看到 0 值字节。无论出于何种原因,紧跟在c
后面的字节包含值 80,这是字符'P'
的 ASCII(或 UTF-8)代码。 下一个字节包含 0(或者有一个字节序列包含不可打印的字符,后跟一个 0 值字节)。
同样,使用&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 );
第四,避免使用"幻数"——其意义超出其直接文字值的数字常量。 如果要与字符值进行比较,请使用字符常量。 欠条,改变
if(input[i] == 32 || input[i] == 9 ){
自
if ( input[i] == ' ' || input[i] == 't' )
这样,您就不必担心字符编码是ASCII,UTF-8,EBCDIC还是其他系统。' '
意味着无处不在的空间,'t'
意味着无处不在的选项卡。
最后。。。
我知道你做这个练习的部分动机是熟悉malloc
和realloc
,但我想提醒你一些事情:
realloc
操作可能成本高昂,它可能会将数据移动到新位置,并且可能会失败。 您真的不想一次realloc
一个字节的缓冲区。 相反,最好分块realloc
。 典型的策略是将当前缓冲区大小乘以 1>的某个系数(通常加倍):
char *tmp = realloc( data, current_size * 2 );
if ( tmp )
{
current_size *= 2;
data = tmp;
}
在尝试访问该内存之前,应始终检查malloc
、calloc
或realloc
调用的结果,以确保其成功。
小文体笔记:
尽可能避免使用全局变量。 没有理由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
...
}