首页 > 解决方案 > 为什么我的程序在运行时会崩溃

问题描述

我必须制作一个程序,通过移动句子来输入要加密的句子。我必须首先将句子转移到一个二维数组,它的大小必须是一个完美的正方形,以最大化所需的空间。(例如,如果句子中的字符数为 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;
}

标签: c

解决方案


首先,你需要学会数数,58-characters你的句子中有数,其次,你让自己变得比需要的更难……

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

  1. 阅读用户输入的句子;
  2. 确定长度;
  3. 修改句子并将所有内容替换spaces'_'(下划线);
  4. 形成一个足以容纳句子中字符的最小大小的方形数组(例如 58 个字符,需要 8x8 数组);
  5. 用修改后的句子填充方形数组,并用下划线填充句子最后一个字符之后的数组的所有空元素;和
  6. 将数组中的每一行左移 1 个字符,将第一个字符移动到最后一个字符先前占据的位置。

您已经包含了提供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,它将读取(包括)'\n'用户按 生成的尾随换行符()Enter。您需要'\n'从句子中删除尾随。您可以通过简单检查 处的字符来做到这一点length - 1。如果它是 a ,只需用nul 字符(或等效的 - )'\n'覆盖它。'\0'0

如果最后一个字符不是'\n',那么您需要检查字符串 ( -1) 的长度是否是您的字符串可以容纳的最大值。如果您已经存储了最大数量的字符,而最后一个字符不是'\n'——这表明用户输入的字符多于你的缓冲区可以容纳的字符——这意味着其中还有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;
    }

len您将如何根据句子的字符数 ( ) 确定所需的方阵大小?长度的平方根(作为整数值)+ 1 非常简单:

    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] = '_';

现在您需要做的就是声明block为 2D VLA(如果您没有 VLA 扩展,您可以声明一个指向字符的指针并分配指针数量并为每个字符size分配一个已分配的字符块size指针使用malloc)。由于您的原始代码使用 VLA,因此我们将采用该方法。

在声明你的 VLA 之后,你需要做的就是用你的修改后的句子来填充它,确保所有其他未使用的元素都是下划线是在你修改后的句子到memset数组'_'之前memcpy的数组,例如:

    char block[size][size];             /* declare VLA */
    memset (block, '_', size * size);   /* set all to '_' */
    memcpy (block, sentence, len);      /* copy sentence to block */

而已。剩下的就是将每一行向左移动 1 个字符。这可以通过简单地将每行中的第一个字符保存在临时变量中,然后使用memmove将行中的每个字符左移 1,然后将行中的最后一个字符设置为您保存的临时字符来完成。(你应该使用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 */
    }

如果我理解正确,这就是完成您在问题中陈述的所有内容。总而言之,您可以执行以下操作:

#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;
}

注意:如果您使用的是windoze,请将所有%zu 格式说明符替换%lu为windows不提供z修饰符size_t

示例使用/输出

$ ./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函数等价物

鉴于您对函数的评论和问题memxxx,以及您是自学的事实,您可能会受益于仅使用简单循环而不是memset,memcpy和的等效代码重写memmove。尽管使用久经考验的真正库函数总是更好,但揭开封面并查看它们内部的功能具有很大的学习价值。

一注。memcpymemmove有效地做同样的事情,但memcpy仅在副本的sourcedestination不重叠的地方定义。memmove对于重叠的区域是安全的。因此,在您的情况下,由于区域重叠,我们需要将元素复制1, 2, 3 ... 8到一个以0, 1, 2 ... 7供使用。memmove

建议 -- 始终在启用警告的情况下编译 -- 使用man pages

此外,在示例之前的最后一个注意事项,始终在启用警告的情况下进行编译,并且在没有警告的情况下编译之前不要接受代码。您应该使用的最少警告是-Wall -Wextra(并且您还应该添加-pedantic一些额外的警告)。我还建议-Wshadow捕获可能导致问题的任何无意的阴影变量。

您应该在每次编译时将这些选项作为编译字符串的一部分进行编译。如果您在使用 VS ( cl.exe) 的 Windows 上,则至少使用/W3(您可以禁用 Windows 上的特定警告,/wdXXXX其中XXXX您希望禁用的警告代码在哪里)

阅读并理解每个警告。是的,花时间阅读和理解每一个。提供问题的简明描述并提供问题发生的行号。去解决每一个。你可以通过听编译器告诉你的内容来学习 C 语言,就像你从大多数教程中学到的一样。

(gcc/clang 警告远远优于 VS 提供的警告,如果启用 VS,/Wall您将获得许多与编译器相关的通用警告,这些警告是非代码特定警告,1/2 打/wdxxxx可以减少到仅与您的代码相关的内容)

正如下面评论中提到的,如果您使用的是 Linux/Unix,请man pages给出每个 C 库函数的详细使用信息。使用它们。只需打开一个终端,然后键入 egman memmove即可了解该memmove功能。当你在学习时(甚至在你认为你已经学习了一个函数之后)man functionname,如果你对它的使用方式或它所采用的正确参数有任何疑问,最重要的是它在成功时返回什么值以及它返回什么值来指示失败(以及是否设置了errno可以通过以下方式检索的附加信息)perror()-- 打印错误)。如果您只是将这些可供您使用的基本工具结合起来,您将减少学习 10 倍的挫败感,并且很快就会领先您的同学数光年......

在这个例子中,这正是上面的代码所做的,但是消除了所有的依赖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)实际操作,您可以使用:

for (i = 0; i < len; i++)           /* copy sentence to block */
    (*block)[i] = sentence[i];      /* (memcpy (block, sentence, len) */

但请注意,这仅适用于len保证小于size * size. 此外,它不适用于char **您为每个字符size指针分配的位置。size为什么?只有数组才能保证所有元素在内存中都是连续的。当使用指针集合和独立分配的内存块来模拟二维数组时,没有这样的保证。

使用/输出完全相同。

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


推荐阅读