为什么我的程序在运行时会崩溃

时间:2018-05-02 03:27:26

标签: c

我必须制作一个程序,通过移动句子来输入要加密的句子。我必须首先将句子转移到2D数组,其大小必须是一个完美的正方形,以最大化所需的空间。 (例如,如果句子中的字符数为54,则数组的大小为8而不是7,因为它适合整个句子)。句子中的空格和空值应替换为下划线。然后我需要向左移动数组,但它会崩溃。

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


void printArray (int size, char block [size][size])
{
    int row, col;
    for (row = 0; row < size; row++)
{
    printf("\n");
    for (col = 0; col < size; col++)
    {
        printf("%c", block[row][col]);
    }
}
}

int main ()
{
// Sentence is "Patience is a minor form of despair disguised as a virtue."

char sentence [1000];
printf("Please enter a sentence to be encrypted: ");
gets (sentence);

int numOfChar = strlen(sentence);
int size = 1;
while (size <= (numOfChar/size))
{
    size++;
}
printf("\nThe number of chars is %d and the size is %d", numOfChar, size);


int n = (size * size) - numOfChar;                      // fills the             
empty values with "_"'s
char fillBlank [n];
int i;
for (i = 0; i < n; i++)
{
    fillBlank[i]  = '_';
}
strcat(sentence, fillBlank);

// ------------------------------------------------ Makes the array ------------------------------------------- //

char block [size][size];
int row;
int col;
int counter = 0;

while (counter < (size * size))
{
    for (row = 0; row < size; row++)
    {
        for (col = 0; col < size; col++)
        {
            if (sentence [counter] == ' ')
                block [row][col] = '_';
            else if (sentence[counter] == '\0')
                block [row][col] = '_';
            else
                block [row][col] = sentence [counter];
            counter++;
        }
    }
}

// ------------------------------------------- Prints the array --------------------------------------------- //

printArray(size, block [size][size]);
/*
for (row = 0; row < size; row++)
{
    printf("\n");
    for (col = 0; col < size; col++)
    {
        printf("%c", block[row][col]);
    }
}
*/
    // ------------------------------------------- Shifts the array left --------------------------------------- //

printf("\n\n\n");
char temp [size];
col = 0;
for (row = 0; row < size; row++)
{
    temp [row] = block [row][col];
}

for (row = 0; row < size; row++)
{
    for (col = 0; col < size; col++)
    {
        block [row][col] = block [row][col+1];
    }
}

col = 7;
for (row = 0; row < size; row++)
{
    block [row][col] = temp [row];
}

printArray(size, block [size][size]);
/*
for (row = 0; row < size; row++)
{
    printf("\n");
    for (col = 0; col < size; col++)
    {
        printf("%c", block[row][col]);
    }
}
*/
return 0;
}

1 个答案:

答案 0 :(得分:1)

首先,你需要学会计算,你的句子中有58-characters,其次,你在自己身上做得比你需要的要难得多......

如果我理解你的问题,你想:

  1. 读取用户输入的句子;
  2. 确定长度;
  3. 修改句子并将所有spaces替换为'_'(下划线);
  4. 形成一个最小尺寸的正方形数组,用于保存句子中的字符(例如58个字符,需要8x8数组);
  5. 用修改后的句子填充方形数组,并在带有下划线的句子的最后一个字符后填充数组的所有空元素;和
  6. 将数组中的每一行移动1个字符,将第1个字符移动到之前占用的位置。
  7. 您已经包含了提供memset memcpymemmove的必要标题 - 这将大大简化您的任务。

    首先,正如评论中提到的那样永远不会永远使用gets 。它是如此不安全,并且很容易被缓冲区溢出利用,它已经完全从C11标准库中删除。请改用fgets,例如:

    #define MAXS 1024   /* if you need a constant - define one (or more) */
    ...    
        char sentence [MAXS] = "";
        size_t i, len = 0, size;
    
        fputs ("Please enter a sentence to be encrypted:\n", stdout);
        if (!fgets (sentence, MAXS, stdin)) {   /* read/validate sentence */
            fputs ("error: user canceled input or EOF\n", stderr);
            return 1;
        }
    

    接下来使用fgets时,它将读取(并包括)用户按 Enter 生成的尾随换行符('\n')。您需要从句子中删除尾随'\n'。您可以通过简单检查length - 1处的角色来完成此操作。如果是'\n',只需使用 nul-character '\0'或简称等效的0)覆盖它。

    如果最后一个字符不是'\n',那么您需要检查字符串的长度(-1)是否是字符串可以容纳的最大长度。如果您已存储了最大字符数,并且最终字符不是'\n' - 这表示用户输入的字符多于缓冲区可容纳的字符数 - 这意味着{{1}中有字符仍然未读 - 相应处理,例如

    stdin

    您如何根据句子的字符数( len = strlen (sentence); /* get sentence length */ if (len && sentence[len - 1] == '\n') /* is last char '\n' ? */ sentence[--len] = 0; /* overwrite with nul-char */ else if (len == MAXS - 1) { /* otherwise - input exceeds MAXS chars */ fprintf (stderr, "error: string exceeds %d chars.\n", MAXS); return 1; } )确定方阵的所需大小?长度的平方根(作为整数值)+ 1非常简单:

    len

    此时您也可以完成对句子的修改,将所有 size = (size_t)sqrt(len); /* set size (trucation intentional) */ if (len % size) size += 1; 替换为spaces,例如

    '_'

    现在您需要做的就是将 for (i = 0; i < len; i++) /* replace ' ' with '_' */ if (sentence[i] == ' ') sentence[i] = '_'; 声明为2D VLA(如果您没有VLA扩展名,则可以声明指向指针的指针并分配{{ 1}}指针的数量,并使用block为每个指针分配size个字符的已分配块。由于您的原始代码使用了VLA,我们将采用这种方式。

    在宣布您的VLA之后,您需要做的就是用修改后的句子来填充它,确保所有其他未使用的元素都是下划线,在size malloc之前将数组memset添加到所有'_'你修改过的句子到数组,例如:

    memcpy

    那就是它。剩下的就是将每一行向左移动1个字符。这可以通过简单地将每行中的第一个字符保存在临时变量中,然后使用 char block[size][size]; /* declare VLA */ memset (block, '_', size * size); /* set all to '_' */ memcpy (block, sentence, len); /* copy sentence to block */ 将行中的每个字符移动1来实现,然后将行中的最后一个字符设置为临时字符保存。 (您应该使用memmove而不是memmove,因为源和目标内存重叠),例如

    memcpy

    如果我理解正确的话,那就是你需要做的就是完成你在问题中陈述的内容。完全放在一起,你可以做类似的事情:

        /* shift the array left */
        for (i = 0; i < size; i++) {
            char tmp = *block[i];           /* save 1st char in row */
            memmove (block[i], &block[i][1], size - 1);  /* shift row left by 1 */
            block[i][size - 1] = tmp;       /* put 1st char as last */
        }
    

    注意:如果您使用的是windoze,请将所有#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define MAXS 1024 /* if you need a constant - define one (or more) */ void printarray (int size, char block [size][size]) { int row, col; for (row = 0; row < size; row++) { for (col = 0; col < size; col++) putchar (block[row][col]); /* don't use printf to output */ putchar ('\n'); /* a single-character */ } } int main (void) { /* sentence is: * "Patience is a minor form of despair disguised as a virtue." */ char sentence [MAXS] = ""; size_t i, len = 0, size; fputs ("Please enter a sentence to be encrypted:\n", stdout); if (!fgets (sentence, MAXS, stdin)) { /* read/validate sentence */ fputs ("error: user canceled input or EOF\n", stderr); return 1; } len = strlen (sentence); /* get sentence length */ if (len && sentence[len - 1] == '\n') /* is last char '\n' ? */ sentence[--len] = 0; /* overwrite with nul-char */ else if (len == MAXS - 1) { /* otherwise - input exceeds MAXS chars */ fprintf (stderr, "error: string exceeds %d chars.\n", MAXS); return 1; } printf ("\nsentence: '%s'\n\n", sentence); /* output sentence */ size = (size_t)sqrt(len); /* set size (trucation intentional) */ if (len % size) size += 1; printf("The number of chars is %zu and the size is %zu\n\n", len, size); for (i = 0; i < len; i++) /* replace ' ' with '_' */ if (sentence[i] == ' ') sentence[i] = '_'; char block[size][size]; /* declare VLA */ memset (block, '_', size * size); /* set all to '_' */ memcpy (block, sentence, len); /* copy sentence to block */ printf ("block array:\n\n"); /* output original block array */ printarray (size, block); /* shift the array left */ for (i = 0; i < size; i++) { char tmp = *block[i]; /* save 1st char in row */ memmove (block[i], &block[i][1], size - 1); /* shift row left by 1 */ block[i][size - 1] = tmp; /* put 1st char as last */ } printf ("\n\nshifted block array:\n\n"); printarray (size, block); return 0; } 格式说明符替换为%zu,因为Windows不提供{{1 } {} {}}修改器

    示例使用/输出

    %lu

    z功能等同物

    鉴于您对size_t函数的评论和问题,以及您自学的事实,您可以通过仅使用简单循环代替{{1}来完成对代码的等效重写。 },$ ./bin/array_block_shift Please enter a sentence to be encrypted: Patience is a minor form of despair disguised as a virtue. sentence: 'Patience is a minor form of despair disguised as a virtue.' The number of chars is 58 and the size is 8 block array: Patience _is_a_mi nor_form _of_desp air_disg uised_as _a_virtu e.______ shifted block array: atienceP is_a_mi_ or_formn of_desp_ ir_disga ised_asu a_virtu_ .______e memxxx。虽然你总是更好地使用一个久经考验的库函数,但是在剥离封面并查看它们在里面做什么方面有很大的学习价值。

    一个注意事项。 memxxxmemset有效地做同样的事情,但memcpy仅定义在副本的memmovememcpy不重叠的地方。 memmove对于重叠的地区是安全的。因此,在您将memcpy左侧复制的元素source复制到destination时,需要使用memmove,因为区域重叠。

    建议 - 始终在启用警告的情况下编译 - 使用1, 2, 3 ... 8

    此外,在示例之前的最后一个注释,始终使用警告启用进行编译,并且在没有警告的情况下编译之前不接受代码。您应该使用的最低警告是0, 1, 2 ... 7(您还应该添加memmove以获得一些其他警告)。我还建议man pages捕获可能导致问题的任何无意的阴影变量。

    每次编译时,都应该使用这些选项进行编译,作为编译字符串的一部分。如果您使用VS(-Wall -Wextra)在Windows上,则至少使用-pedantic(您可以在-Wshadow的窗口上禁用特定警告,其中cl.exe是您希望的警告代码禁用)

    阅读并理解每个警告。是的,花点时间阅读并理解每一个。提供问题的简明描述并提供出现问题的行号。去每个地址。只需通过听大多数教程中编译器告诉你的内容,你就可以学到同样多的C语言。

    (gcc / clang警告远远优于VS提供的警告,如果启用/W3,则使用VS,您将获得许多与通用编译器相关的警告,这些警告是非代码特定的警告,其中包含1 / 2打/wdXXXX可以简化为与您的代码相关的内容)

    如下面的评论中所述,如果您使用的是Linux / Unix,XXXX会提供有关每个C库函数的详细用法信息。使用它们。只需打开一个终端,然后输入例如/Wall即可了解/wdxxxx功能。当你正在学习时(甚至在你认为自己已经学会了一项功能之后),如果你对它的使用方法或正确的参数有任何疑问,请务必咨询man pages最重要的是它在成功时返回的值以及它返回的值表示失败(以及是否在man memmove中设置了可以使用memmove检索的其他信息 - 打印错误)。如果您只是使用这些可供您使用的基本工具,您将减少10倍的学习挫折感,并且很快就会比您的同学快几年......

    在示例中,这与上面的代码完全相同,但消除了对man functionname的所有依赖(这将让您了解为什么它们可以简化您的生活)

    errno

    注意:要更准确地模拟perror()实际执行的操作,您可以使用:

    memxxx

    但请注意,这仅适用于保证#include <stdio.h> #include <string.h> #define MAXS 1024 /* if you need a constant - define one (or more) */ void printarray (int size, char block [size][size]) { int row, col; for (row = 0; row < size; row++) { for (col = 0; col < size; col++) putchar (block[row][col]); /* don't use printf to output */ putchar ('\n'); /* a single-character */ } } int main (void) { /* sentence is: * "Patience is a minor form of despair disguised as a virtue." */ char sentence [MAXS] = ""; size_t i, j, len = 0, size; fputs ("Please enter a sentence to be encrypted:\n", stdout); if (!fgets (sentence, MAXS, stdin)) { /* read/validate sentence */ fputs ("error: user canceled input or EOF\n", stderr); return 1; } len = strlen (sentence); /* get sentence length */ if (len && sentence[len - 1] == '\n') /* is last char '\n' ? */ sentence[--len] = 0; /* overwrite with nul-char */ else if (len == MAXS - 1) { /* otherwise - input exceeds MAXS chars */ fprintf (stderr, "error: string exceeds %d chars.\n", MAXS); return 1; } printf ("\nsentence: '%s'\n\n", sentence); /* output sentence */ if (len < 4) { fputs ("error: sentence less than 4 chars - too short.\n", stderr); return 1; } for (size = 2; size * size < len; size++) {} /* set size */ printf("The number of chars is %zu and the size is %zu\n\n", len, size); for (i = 0; i < len; i++) /* replace ' ' with '_' */ if (sentence[i] == ' ') sentence[i] = '_'; char block[size][size]; /* declare VLA */ for (i = 0; i < size; i++) /* initialize all element '_' */ for (j = 0; j < size; j++) /* (memset (block, '_', size) */ block[i][j] = '_'; size_t n = 0; for (i = 0; i < size; i++) /* copy sentence to block */ for (j = 0; j < size; j++) /* (memcpy (block, sentence, len) */ if (n < len) block[i][j] = sentence[n++]; else break; printf ("block array:\n\n"); /* output original block array */ printarray (size, block); /* shift the array left (memmove (block[i], &block[i][1], size - 1)) */ for (i = 0; i < size; i++) { char tmp = block[i][0]; /* save 1st char in row */ for (j = 1; j < size; j++) block[i][j-1] = block[i][j]; block[i][size - 1] = tmp; /* put 1st char as last */ } printf ("\n\nshifted block array:\n\n"); printarray (size, block); return 0; } 小于memcpy (block, sentence, len)的2D数组。此外,它不适用于for (i = 0; i < len; i++) /* copy sentence to block */ (*block)[i] = sentence[i]; /* (memcpy (block, sentence, len) */ ,每个len分配size * sizechar **个字符的指针。为什么?只有数组才能保证所有元素在内存中都是连续的。使用指针集合和独立分配的内存块来模拟2D数组时,没有这样的保证。

    使用/输出完全相同。

    仔细看看,如果我误解了你的目标,或者你还有其他问题,请告诉我。