在 C 中使用指针算术后,这个奇怪的输出是什么?
What is this weird output after using pointer Arithemtic in C?
我在代码中的目标是将某种输入解析为关于所有 space 的词,但同时使用那些 space 来表示词的变化。这里的逻辑是,任何时候遇到 space 它都会循环,直到不再有 space 字符,然后当它遇到一个单词时,它会循环直到遇到 space 字符或 ' \0' 同时将每个字符放入二维数组中数组内部数组的一个索引中。然后在 while 循环再次继续之前它索引到下一个数组。
我几乎可以肯定逻辑的实现足够好,可以正常工作,但是我得到了下面列出的这个奇怪的输出我以前遇到过同样的问题,当弄乱指针和诸如此类的东西时,但我就是不能得到这个无论我做什么,都要工作。关于为什么我真的很好奇背后的原因有什么想法吗?
#include <stdio.h>
#include <stdlib.h>
void print_mat(char **arry, int y, int x){
for(int i=0;i<y;i++){
for(int j=0;j<x;j++){
printf("%c",arry[i][j]);
}
printf("\n");
}
}
char **parse(char *str)
{
char **parsed=(char**)malloc(sizeof(10*sizeof(char*)));
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
}
char **pointer = parsed;
while(*str!='[=10=]'){
if(*str==32)
{
while(*str==32 && *str!='[=10=]'){
str++;
}
}
while(*str!=32 && *str!='[=10=]'){
(*pointer) = (str);
(*pointer)++;
str++;
}
pointer++;
}
return parsed;
}
int main(){
char str[] = "command -par1 -par2 thething";
char**point=parse(str);
print_mat(point,10,200);
return 0;
}
-par1 -par2 thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
-par2 thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
UP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
我也尝试简单地索引二维数组但无济于事
char **parse(char *str)
{
int i, j;
i=0;
j=0;
char **parsed=(char**)malloc(sizeof(10*sizeof(char*)));
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
}
while(*str!='[=12=]'){
i=0;
if(*str==32)
{
while(*str==32 && *str!='[=12=]'){
str++;
}
}
while(*str!=32 && *str!='[=12=]'){
parsed[j][i] = (*str);
i++;
str++;
}
j++;
}
return parsed;
}
输出:
command�&�v�U`'�v�U0(�v�U)�v�U�)�v�U
-par1
-par2
thething
makefile:5: recipe for target 'build' failed
make: *** [build] Segmentation fault (core dumped)
您的代码中有几个问题:
- 您的程序正在泄漏内存。
- 您的程序正在访问不属于它的内存,这是 UB。
让我们一一讨论 -
第一个问题 - 内存泄漏:
检查 parse()
函数的这一部分:
while(*str!=32 && *str!='[=10=]'){
(*pointer) = (str);
在外部 while
循环的第一次迭代中,*pointer
将为您提供 parsed
数组的第一个成员,即 parsed[0]
,它是指向 char
。请注意,在外部 while
循环之前,您正在动态地将内存分配给 parsed[0]
、parsed[1]
... parsed[9]
中的 parse()
指针。在内部 while
循环中,您将它们指向 str
。因此,他们将丢失动态分配的内存引用并导致内存泄漏。
第二个问题 - 访问不属于它的内存:
如上所述,指针 parsed[0]
、parsed[1]
等将指向 str
的内部 while
循环中的任何当前值 parse()
函数。这意味着,指针 parsed[0]
、parsed[1]
等将指向数组 str
(在 main()
中定义)的某个元素。在 print_mat()
函数中,您正在传递 200
并访问数组 arry
从 0
到 199
索引的每个指针。因为,arry
指针指向大小为 29
的 str
数组,这意味着,您的程序正在访问超出其 UB 大小的内存(数组)。
让我们在不做太多更改的情况下解决代码中的这些问题:
内存泄漏:
不是将指针指向 str
,而是将 str
的字符分配给分配的内存,如下所示:
int i = 0;
while(*str!=32 && *str!='[=11=]'){
(*pointer)[i++] = (*str);
str++;
}
访问不属于它的内存:
你应该记住的一点:
在 C 中,字符串实际上是 one-dimensional 以空字符 [=50=]
.
结尾的字符数组
首先,动态分配内存后清空字符串,以便在打印时识别未使用的指针:
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
parsed[i][0] = '[=12=]';
}
在将单词写入 parsed
数组指针后,用空终止符终止所有字符串:
int i = 0;
while(*str!=32 && *str!='[=13=]'){
(*pointer)[i++] = (*str);
str++;
}
// Add null terminator
(*pointer)[i] = '[=13=]';
在 print_mat()
中,确保一旦您点击了空终止符,就不要超出它。修改内for
循环条件:
for(int j = 0; (j < x) && (arry[i][j] != '[=14=]'); j++){
printf("%c",arry[i][j]);
你不需要逐个字符地打印字符串,你可以简单地使用 %s
格式说明符来打印一个字符串,就像这样 -
for (int i = 0;i < y; i++) {
if (arry[i][0] != '[=15=]') {
printf ("%s\n", arry[i]);
}
}
根据上述建议的更改(这是您的程序正常运行所需的最小更改),您的代码将如下所示:
#include <stdio.h>
#include <stdlib.h>
void print_mat (char **arry, int y) {
for (int i = 0; i < y; i++) {
if (arry[i][0] != '[=16=]') {
printf ("%s\n", arry[i]);
}
}
}
char **parse(char *str) {
char **parsed = (char**)malloc(sizeof(10*sizeof(char*)));
// check malloc return
for(int i = 0; i < 10; i++){
parsed[i] = (char*)malloc(200*sizeof(char));
// check malloc return
parsed[i][0] = '[=16=]';
}
char **pointer = parsed;
while (*str != '[=16=]') {
if(*str == 32) {
while(*str==32 && *str!='[=16=]') {
str++;
}
}
int i = 0;
while (*str != 32 && *str != '[=16=]') {
(*pointer)[i++] = (*str);
str++;
}
(*pointer)[i] = '[=16=]';
pointer++;
}
return parsed;
}
int main (void) {
char str[] = "command -par1 -par2 thething";
char **point = parse(str);
print_mat (point, 10);
// free the dynamically allocate memory
return 0;
}
输出:
command
-par1
-par2
thething
您的代码实现可以做很多改进,例如-
- 如上所示,您可以使用
%s
格式说明符而不是逐字符打印字符串等。我将由您来识别这些更改并修改您的程序。
- 仅在
str
. 中有单词的地方将内存分配给 parsed
数组指针
- 不是将固定大小的内存(即
200
)分配给parsed
数组指针,而是仅分配字大小的内存。
几点建议:
- 始终检查 return 函数的值,例如
malloc
。
- 确保在程序完成后释放动态分配的内存。
您可以通过更简单的方式实现您想要的。
首先,定义一个函数来检查字符(分隔符)列表中是否存在字符(分隔符):
// Returns true if c is found in a list of separators, false otherwise.
bool belongs(const char c, const char *list)
{
for (const char *p = list; *p; ++p)
if (*p == c) return true;
return false;
}
然后,定义一个函数,将给定的字符串拆分为由一个或多个分隔符分隔的标记:
// Splits a string into into tokens, separated by one of the separators in sep
bool split(const char *s, const char *sep, char **tokens, size_t *ntokens, const size_t maxtokens)
{
// Start with zero tokens.
*ntokens = 0;
const char *start = s, *end = s;
for (const char *p = s; /*no condtition*/; ++p) {
// Can no longer hold more tokens? Exit.
if (*ntokens == maxtokens)
return false;
// Not a token? Continue looping.
if (*p && !belongs(*p, sep))
continue;
// Found a token: calculate its length.
size_t tlength = p - start;
// Empty token?
if (tlength == 0) {
// And reached the end of string? Break.
if (!*p) break;
// Not the end of string? Skip it.
++start;
continue;
}
// Attempt to allocate memory.
char *token = malloc(sizeof(*token) * (tlength + 1));
// Failed? Exit.
if (!token)
return false;
// Copy the token.
strncpy(token, start, tlength+1);
token[tlength] = '[=11=]';
// Put it in tokens array.
tokens[*ntokens] = token;
// Update the number of tokens.
*ntokens += 1;
// Reached the end of string? Break.
if (!*p) break;
// There is more to parse. Set the start to the next char.
start = p + 1;
}
return true;
}
这样称呼它:
int main(void)
{
char command[] = "command -par1 -par2 thing";
const size_t maxtokens = 10;
char **tokens = malloc(sizeof *tokens * maxtokens);
if (!tokens) return 1;
size_t ntokens = 0;
split(command, " ", tokens, &ntokens, maxtokens);
// Print all tokens.
printf("Number of tokens = %ld\n", ntokens);
for (size_t i = 0; i < ntokens; ++i)
printf("%s\n", tokens[i]);
// Release memory when done.
for (size_t i = 0; i < ntokens; ++i)
free(tokens[i]);
free(tokens);
}
输出:
Number of tokens = 4
command
-par1
-par2
thing
我在代码中的目标是将某种输入解析为关于所有 space 的词,但同时使用那些 space 来表示词的变化。这里的逻辑是,任何时候遇到 space 它都会循环,直到不再有 space 字符,然后当它遇到一个单词时,它会循环直到遇到 space 字符或 ' \0' 同时将每个字符放入二维数组中数组内部数组的一个索引中。然后在 while 循环再次继续之前它索引到下一个数组。
我几乎可以肯定逻辑的实现足够好,可以正常工作,但是我得到了下面列出的这个奇怪的输出我以前遇到过同样的问题,当弄乱指针和诸如此类的东西时,但我就是不能得到这个无论我做什么,都要工作。关于为什么我真的很好奇背后的原因有什么想法吗?
#include <stdio.h>
#include <stdlib.h>
void print_mat(char **arry, int y, int x){
for(int i=0;i<y;i++){
for(int j=0;j<x;j++){
printf("%c",arry[i][j]);
}
printf("\n");
}
}
char **parse(char *str)
{
char **parsed=(char**)malloc(sizeof(10*sizeof(char*)));
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
}
char **pointer = parsed;
while(*str!='[=10=]'){
if(*str==32)
{
while(*str==32 && *str!='[=10=]'){
str++;
}
}
while(*str!=32 && *str!='[=10=]'){
(*pointer) = (str);
(*pointer)++;
str++;
}
pointer++;
}
return parsed;
}
int main(){
char str[] = "command -par1 -par2 thething";
char**point=parse(str);
print_mat(point,10,200);
return 0;
}
-par1 -par2 thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
-par2 thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
thethingUP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
UP%�W���U�6o� X%��U�v;,���UP%���cNjW��]A�aW�Ӹto�8so�z�
我也尝试简单地索引二维数组但无济于事
char **parse(char *str)
{
int i, j;
i=0;
j=0;
char **parsed=(char**)malloc(sizeof(10*sizeof(char*)));
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
}
while(*str!='[=12=]'){
i=0;
if(*str==32)
{
while(*str==32 && *str!='[=12=]'){
str++;
}
}
while(*str!=32 && *str!='[=12=]'){
parsed[j][i] = (*str);
i++;
str++;
}
j++;
}
return parsed;
}
输出:
command�&�v�U`'�v�U0(�v�U)�v�U�)�v�U
-par1
-par2
thething
makefile:5: recipe for target 'build' failed
make: *** [build] Segmentation fault (core dumped)
您的代码中有几个问题:
- 您的程序正在泄漏内存。
- 您的程序正在访问不属于它的内存,这是 UB。
让我们一一讨论 -
第一个问题 - 内存泄漏:
检查 parse()
函数的这一部分:
while(*str!=32 && *str!='[=10=]'){
(*pointer) = (str);
在外部 while
循环的第一次迭代中,*pointer
将为您提供 parsed
数组的第一个成员,即 parsed[0]
,它是指向 char
。请注意,在外部 while
循环之前,您正在动态地将内存分配给 parsed[0]
、parsed[1]
... parsed[9]
中的 parse()
指针。在内部 while
循环中,您将它们指向 str
。因此,他们将丢失动态分配的内存引用并导致内存泄漏。
第二个问题 - 访问不属于它的内存:
如上所述,指针 parsed[0]
、parsed[1]
等将指向 str
的内部 while
循环中的任何当前值 parse()
函数。这意味着,指针 parsed[0]
、parsed[1]
等将指向数组 str
(在 main()
中定义)的某个元素。在 print_mat()
函数中,您正在传递 200
并访问数组 arry
从 0
到 199
索引的每个指针。因为,arry
指针指向大小为 29
的 str
数组,这意味着,您的程序正在访问超出其 UB 大小的内存(数组)。
让我们在不做太多更改的情况下解决代码中的这些问题:
内存泄漏:
不是将指针指向 str
,而是将 str
的字符分配给分配的内存,如下所示:
int i = 0;
while(*str!=32 && *str!='[=11=]'){
(*pointer)[i++] = (*str);
str++;
}
访问不属于它的内存:
你应该记住的一点:
在 C 中,字符串实际上是 one-dimensional 以空字符 [=50=]
.
首先,动态分配内存后清空字符串,以便在打印时识别未使用的指针:
for(int i=0;i<10;i++){
parsed[i]=(char*)malloc(200*sizeof(char));
parsed[i][0] = '[=12=]';
}
在将单词写入 parsed
数组指针后,用空终止符终止所有字符串:
int i = 0;
while(*str!=32 && *str!='[=13=]'){
(*pointer)[i++] = (*str);
str++;
}
// Add null terminator
(*pointer)[i] = '[=13=]';
在 print_mat()
中,确保一旦您点击了空终止符,就不要超出它。修改内for
循环条件:
for(int j = 0; (j < x) && (arry[i][j] != '[=14=]'); j++){
printf("%c",arry[i][j]);
你不需要逐个字符地打印字符串,你可以简单地使用 %s
格式说明符来打印一个字符串,就像这样 -
for (int i = 0;i < y; i++) {
if (arry[i][0] != '[=15=]') {
printf ("%s\n", arry[i]);
}
}
根据上述建议的更改(这是您的程序正常运行所需的最小更改),您的代码将如下所示:
#include <stdio.h>
#include <stdlib.h>
void print_mat (char **arry, int y) {
for (int i = 0; i < y; i++) {
if (arry[i][0] != '[=16=]') {
printf ("%s\n", arry[i]);
}
}
}
char **parse(char *str) {
char **parsed = (char**)malloc(sizeof(10*sizeof(char*)));
// check malloc return
for(int i = 0; i < 10; i++){
parsed[i] = (char*)malloc(200*sizeof(char));
// check malloc return
parsed[i][0] = '[=16=]';
}
char **pointer = parsed;
while (*str != '[=16=]') {
if(*str == 32) {
while(*str==32 && *str!='[=16=]') {
str++;
}
}
int i = 0;
while (*str != 32 && *str != '[=16=]') {
(*pointer)[i++] = (*str);
str++;
}
(*pointer)[i] = '[=16=]';
pointer++;
}
return parsed;
}
int main (void) {
char str[] = "command -par1 -par2 thething";
char **point = parse(str);
print_mat (point, 10);
// free the dynamically allocate memory
return 0;
}
输出:
command
-par1
-par2
thething
您的代码实现可以做很多改进,例如-
- 如上所示,您可以使用
%s
格式说明符而不是逐字符打印字符串等。我将由您来识别这些更改并修改您的程序。 - 仅在
str
. 中有单词的地方将内存分配给 - 不是将固定大小的内存(即
200
)分配给parsed
数组指针,而是仅分配字大小的内存。
parsed
数组指针
几点建议:
- 始终检查 return 函数的值,例如
malloc
。 - 确保在程序完成后释放动态分配的内存。
您可以通过更简单的方式实现您想要的。
首先,定义一个函数来检查字符(分隔符)列表中是否存在字符(分隔符):
// Returns true if c is found in a list of separators, false otherwise.
bool belongs(const char c, const char *list)
{
for (const char *p = list; *p; ++p)
if (*p == c) return true;
return false;
}
然后,定义一个函数,将给定的字符串拆分为由一个或多个分隔符分隔的标记:
// Splits a string into into tokens, separated by one of the separators in sep
bool split(const char *s, const char *sep, char **tokens, size_t *ntokens, const size_t maxtokens)
{
// Start with zero tokens.
*ntokens = 0;
const char *start = s, *end = s;
for (const char *p = s; /*no condtition*/; ++p) {
// Can no longer hold more tokens? Exit.
if (*ntokens == maxtokens)
return false;
// Not a token? Continue looping.
if (*p && !belongs(*p, sep))
continue;
// Found a token: calculate its length.
size_t tlength = p - start;
// Empty token?
if (tlength == 0) {
// And reached the end of string? Break.
if (!*p) break;
// Not the end of string? Skip it.
++start;
continue;
}
// Attempt to allocate memory.
char *token = malloc(sizeof(*token) * (tlength + 1));
// Failed? Exit.
if (!token)
return false;
// Copy the token.
strncpy(token, start, tlength+1);
token[tlength] = '[=11=]';
// Put it in tokens array.
tokens[*ntokens] = token;
// Update the number of tokens.
*ntokens += 1;
// Reached the end of string? Break.
if (!*p) break;
// There is more to parse. Set the start to the next char.
start = p + 1;
}
return true;
}
这样称呼它:
int main(void)
{
char command[] = "command -par1 -par2 thing";
const size_t maxtokens = 10;
char **tokens = malloc(sizeof *tokens * maxtokens);
if (!tokens) return 1;
size_t ntokens = 0;
split(command, " ", tokens, &ntokens, maxtokens);
// Print all tokens.
printf("Number of tokens = %ld\n", ntokens);
for (size_t i = 0; i < ntokens; ++i)
printf("%s\n", tokens[i]);
// Release memory when done.
for (size_t i = 0; i < ntokens; ++i)
free(tokens[i]);
free(tokens);
}
输出:
Number of tokens = 4
command
-par1
-par2
thing