c - 为什么我的程序在运行时会崩溃
问题描述
我必须制作一个程序,通过移动句子来输入要加密的句子。我必须首先将句子转移到一个二维数组,它的大小必须是一个完美的正方形,以最大化所需的空间。(例如,如果句子中的字符数为 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;
}
解决方案
首先,你需要学会数数,58-characters
你的句子中有数,其次,你让自己变得比需要的更难……
如果我理解你的问题,你想:
- 阅读用户输入的句子;
- 确定长度;
- 修改句子并将所有内容替换
spaces
为'_'
(下划线); - 形成一个足以容纳句子中字符的最小大小的方形数组(例如 58 个字符,需要 8x8 数组);
- 用修改后的句子填充方形数组,并用下划线填充句子最后一个字符之后的数组的所有空元素;和
- 将数组中的每一行左移 1 个字符,将第一个字符移动到最后一个字符先前占据的位置。
您已经包含了提供memset
memcpy
和memmove
-- 这将大大简化您的任务的必要标题。
首先,正如评论中提到的,永远,永远,永远使用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
。尽管使用久经考验的真正库函数总是更好,但揭开封面并查看它们内部的功能具有很大的学习价值。
一注。memcpy
并memmove
有效地做同样的事情,但memcpy
仅在副本的source
和destination
不重叠的地方定义。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
为什么?只有数组才能保证所有元素在内存中都是连续的。当使用指针集合和独立分配的内存块来模拟二维数组时,没有这样的保证。
使用/输出完全相同。
如果我误解了您的目标,或者您还有其他问题,请仔细查看并告诉我。
推荐阅读
- azure - Microsoft Azure 程序,例如 BizSpark
- excel - 使用 VBA 如何将 API 数据集从 HTTP GET 函数加载到 Excel 中?
- postgresql - 从 Spark 连接到 Postgres 时出现 NullpointerException - 为什么?
- java - 如何在不完成活动的情况下从最近的列表返回到最后一个应用程序
- azure - Azure 文件共享是否支持病毒扫描?
- google-chrome - Burp 拦截不适用于 Chrome 中的本地主机
- r - 根据行标签检索对应的列值
- c# - 保存虚拟滚动位置
- azureservicebus - 将 Rebus 与 Azure 服务总线、队列和主题一起使用的代码示例
- ruby - 如何将变量设置为if条件