我必须制作一个程序,通过移动句子来输入要加密的句子。我必须首先将句子转移到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;
}
答案 0 :(得分:1)
首先,你需要学会计算,你的句子中有58-characters
,其次,你在自己身上做得比你需要的要难得多......
如果我理解你的问题,你想:
spaces
替换为'_'
(下划线); 您已经包含了提供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
时,它将读取(并包括)用户按 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
。虽然你总是更好地使用一个久经考验的库函数,但是在剥离封面并查看它们在里面做什么方面有很大的学习价值。
一个注意事项。 memxxx
和memset
有效地做同样的事情,但memcpy
仅定义在副本的memmove
和memcpy
不重叠的地方。 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 * size
个char **
个字符的指针。为什么?只有数组才能保证所有元素在内存中都是连续的。使用指针集合和独立分配的内存块来模拟2D数组时,没有这样的保证。
使用/输出完全相同。
仔细看看,如果我误解了你的目标,或者你还有其他问题,请告诉我。