C-为字符串中的每个字符打印ASCII值

时间:2019-10-03 06:54:12

标签: c arrays for-loop ascii

我是C的新手,我正在尝试编写一个程序,该程序以用户输入的名称为每个字母打印ASCII值。我试图将字母存储在数组中,并尝试分别打印每个ASCII值和名称字母,但是由于某种原因,它仅打印第一个字母的值。

例如,如果我写“ Anna”,它只会打印65,而不显示名称中其他字母的值。我认为这与sizeof(name)/sizeof(char)循环的for部分有关,因为当我单独打印它时,它只会打印出1。

我不知道如何解决它:

#include <stdio.h>

int main(){

    int e;
    char name[] = "";
    printf("Enter a name : \n");
    scanf("%c",&name);
    for(int i = 0; i < (sizeof(name)/sizeof(char)); i++){
        e = name[i];
        printf("The ASCII value of the letter %c is : %d \n",name[i],e);
    }
    int n = (sizeof(name)/sizeof(char));
    printf("%d", n);
}

2 个答案:

答案 0 :(得分:2)

这是经过纠正的带注释的版本:

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

int main() {
    int e;
    char name[100] = ""; // Allow for up to 100 characters
    printf("Enter a name : \n");
//  scanf("%c", &name); // %c reads a single character
    scanf("%99s", name); // Use %s to read a string! %99s to limit input size! 
//  for (int i = 0; i < (sizeof(name) / sizeof(char)); i++) { // sizeof(name) / sizeof(char) is a fixed value!
    size_t len = strlen(name); // Use this library function to get string length
    for (size_t i = 0; i < len; i++) { // Saves calculating each time!
        e = name[i];
        printf("The ASCII value of the letter %c is : %d \n", name[i], e);
    }
    printf("\n Name length = %zu\n", strlen(name)); // Given length!
    int n = (sizeof(name) / sizeof(char)); // As noted above, this will be ...
    printf("%d", n);                 // ... a fixed value (100, as it stands).
    return 0; // ALWAYS return an integer from main!
}

还要阅读您的问题中给出的评论!

答案 1 :(得分:2)

这是一个相当长的答案,请跳到最后的代码示例。


首先,通过初始化一个未指定长度的char数组,您将使该数组的长度为1(它仅包含空字符串)。这里的关键问题是C语言中的数组是固定大小的,因此名称不会变大。

第二,格式说明符%c导致scanf仅读取一个字节。这意味着即使您创建了一个更大的数组,无论如何您也只会读取一个字节。

您为scanf提供的参数是错误的,但意外地起作用-当您期望指向char的指针时,您正在将指针传递给数组。之所以有效,是因为指向数组的指针指向数组的第一个元素。幸运的是,这很容易解决,可以将一个类型的数组传递给需要该类型指针的函数-据说它“衰减”了一个指针。因此,您可以改为通过name

由于这两个动作,您现在遇到name的长度为1的情况,并且您已经精确地读取了一个字节。下一个问题是sizeof(name)/sizeof(char)-在您的程序中它将始终等于1。 sizeof char被定义为始终等于1,因此将其用作除数不会产生任何效果,并且我们已经知道sizeof name等于1。这意味着您的for循环将仅从数组中读取一个字节。 。出于完全相同的原因,n等于1。这本身并不是错误的,它可能不是您所期望的。


可以通过两种方法解决此问题,但我将展示一种方法。首先,您不希望像这样初始化name,因为它总是创建一个大小为1的数组。相反,您想为该数组手动指定一个更大的大小,例如100字节(其中最后一个将专用于终止的空字节。

char name[100];
/* You might want to zero out the array too by eg. using memset. It's not
   necessary in this case, but arrays are allowed to contain anything unless
   and until you replace their contents.

   Parameters are target, byte to fill it with, and amount of bytes to fill */
memset(name, 0, sizeof(name));

第二,如果您仅从标准输入中读取字节字符串而不是更复杂的格式化字符串,则根本不必使用scanf。你可以例如。使用fgets从标准输入中读取整行,尽管其中还包括换行符,我们必须将其删除。

/* The parameters are target to write to, bytes to write, and file to read from.

   fgets writes a null terminator automatically after the string, so we will
   read at most sizeof(name) - 1 bytes.
*/
fgets(name, sizeof(name), stdin);

现在,您已将名称读入内存。但是name数组的大小没有改变,因此,如果您按原样使用其余代码,则会收到许多消息,提示The ASCII value of the letter is : 0。为了获得有意义的字符串长度,我们将使用strlen

注意:strlen通常在不正确以null终止的任意字符串上使用都是不安全的,因为它将一直读取直到找到零字节,但是我们只能得到一个可移植的边界检查版本{{ 1}}。在这种情况下,我们还知道该字符串是空终止的,因为strnlen_s对此进行了处理。

fgets

现在我们有了字符串的长度,我们可以检查最后一个字节是否为换行符,如果是,请将其删除。

/* size_t is a large, unsigned integer type big enough to contain the
   theoretical maximum size of an object, so size functions often return
   size_t.

   strlen counts the amount of bytes before the first null (0) byte */
size_t n = strlen(name);

循环看起来非常相似,因为开始时基本上没问题。通常,我们希望避免比较带符号的/* Assuming every line ends with a newline, we can simply zero out the last byte if it's '\n' */ if (name[n - 1] == '\n') { name[n - 1] = '\0'; /* The string is now 1 byte shorter, because we removed the newline. We don't need to calculate strlen again, we can just do it manually. */ --n; } 和未签名的int所引起的问题,因此我们也将使size_t的类型为i

size_t

将它们放在一起,我们得到

for (size_t i = 0; i < n; i++) {
    int e = name[i];
    printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}

应该可以按预期工作。


其他说明:

  • 此代码应该是有效的C99,但我认为它不是有效的C89。如果需要编写较旧的标准,则需要做一些不同的事情。幸运的是,如果您告诉编译器您要使用哪种标准,则编译器应警告您这些问题。如今,C99可能是默认值,但是较旧的代码仍然存在。

  • 像这样将字符串读取到固定大小的缓冲区中有点不灵活,因此在实际情况下,您可能需要一种必要时动态增加缓冲区大小的方法。这可能需要您使用C的手动内存管理功能,例如#include <stdio.h> #include <string.h> int main() { char name[100]; memset(name, 0, sizeof(name)); printf("Enter a name : \n"); fgets(name, sizeof(name), stdin); size_t n = strlen(name); if (n > 0 && name[n - 1] == '\n') { name[n - 1] = '\0'; --n; } for (size_t i = 0; i < n; i++){ int e = name[i]; printf("The ASCII value of the letter %c is : %d \n", name[i], e); } /* To correctly print a size_t, use %zu */ printf("%zu\n", n); /* In C99 main implicitly returns 0 if you don't add a return value yourself, but it's a good habit to remember to return from functions. */ return 0; } malloc,这些功能并不是特别困难,但要格外小心,以免出现内存泄漏之类的问题。

  • 不能保证您正在读取的字符串采用任何特定的编码,并且C字符串对于处理未采用单字节编码的文本并不是很理想。支持“宽字符串”,但您可能更经常地处理包含UTF-8的realloc字符串,其中单个代码点可能是多个字节,甚至可能不代表单个字母。在更通用的程序中,您应该记住这一点。