我一直在墙上撞了太多时间,我需要你的帮助。在我的作业中,我应该编写一个函数,将一个字符串拆分为由空格分隔的标记。这些标记被复制到动态分配的字符串数组中。该字符串作为参数传递,第二个参数是字符串数组的指针变量(char *** argv)。我很难理解如何处理这个三维数组以及如何动态分配它。以下是相关代码:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char **args = NULL;
char cmdline[] = "cmdline -s 20 -r -t parameter -p 20 filename";
int count = parse_cmdline(&args, cmdline);
这就是我想出来的:
#include <stdlib.h>
#include <string.h>
/* Parses a string into tokens (command line parameters) separated by space
* Builds a dynamically allocated array of strings. Pointer to the array is
* stored in variable pointed by argv.
*
* Parameters:
* argv: pointer to the variable that will store the string array
* input: the string to be parsed (the original string can be modified, if needed)
*
* Returns:
* number of space-separated tokens in the string */
int parse_cmdline(char ***argv, char *input)
{
int i=0;
char *token=strtok(input," ");
while (token!=NULL) {
*argv=realloc(*argv,(i+1)*sizeof(char*));
*argv[i]=malloc(sizeof(token));
memcpy(*argv[i],token,sizeof(token));
i++;
token=strtok(NULL," ");
}
return i;
}
Valgrind给出了这个输出:
==358== Use of uninitialised value of size 8
==358== at 0x40263B: parse_cmdline (cmdline.c:21)
==358== by 0x40155E: test_parse_cmdline (test_source.c:19)
==358== by 0x405670: srunner_run_all (in /tmc/test/test)
==358== by 0x40221E: tmc_run_tests (tmc-check.c:121)
==358== by 0x401ED7: main (test_source.c:133)
==358== Uninitialised value was created by a stack allocation
==358== at 0x401454: test_parse_cmdline (test_source.c:10)
==358==
==358== Invalid write of size 8
==358== at 0x40263B: parse_cmdline (cmdline.c:21)
==358== by 0x40155E: test_parse_cmdline (test_source.c:19)
==358== by 0x405670: srunner_run_all (in /tmc/test/test)
==358== by 0x40221E: tmc_run_tests (tmc-check.c:121)
==358== by 0x401ED7: main (test_source.c:133)
==358== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==358==
==358==
==358== Process terminating with default action of signal 11 (SIGSEGV)
==358== Access not within mapped region at address 0x0
==358== at 0x40263B: parse_cmdline (cmdline.c:21)
==358== by 0x40155E: test_parse_cmdline (test_source.c:19)
==358== by 0x405670: srunner_run_all (in /tmc/test/test)
==358== by 0x40221E: tmc_run_tests (tmc-check.c:121)
==358== by 0x401ED7: main (test_source.c:133)
==358== If you believe this happened as a result of a stack
==358== overflow in your program's main thread (unlikely but
==358== possible), you can try to increase the size of the
==358== main thread stack using the --main-stacksize= flag.
==358== The main thread stack size used in this run was 8388608.
我一遍又一遍地阅读指针,字符串,数组和多维数组,但我似乎无法理解它。我真的不明白的一件事是,为什么指针传递为(&amp; args),为什么不把它作为数组的指针传递?我不确定我是否正在使用memcpy。
答案 0 :(得分:3)
除了使用sizeof
而不是strlen
之类的错误之外,我们先来看看其他几个问题。
&args
?我真的不明白的一件事是,为什么指针传递为(&amp; args),为什么不把它作为数组的指针传递?
看看你的陈述:
char **args = NULL;
这告诉你什么?
char **args
1. char args => args is char.
2. char *args => args is pointer to char.
3. char **args => args is pointer to pointer to char.
char **args = NULL;
它被初始化并指向NULL。在您的函数中,您将其指向的内容从NULL更新为 realloc 返回的新地址。
这是非常重要的一点:你更新它指向的内容!如果您没有传递 args 的地址,则无法告知主要位置的主要位置。
您处理传递内容的副本。
如果 指向某个地址,例如从以前的 malloc 中,您可以更新它指向的内容,但是您无法 realloc ,因为您无法更新指针本身。那就是:回到 main ,它仍然会指向旧位置。
你的目标是:
args * * 0x123e0a -> [0] 0x123fa00 -> 0x12fbae0 - 0x12fbae8 cmdline [1] 0x123fa08 -> 0x12fbae9 - 0x12fbaec -s [2] 0x123fa10 -> 0x12fcae0 - 0x12fcae3 20 [3] 0x123fa18 -> 0x12fbad8 - 0x12fbadb -r ...
如果我们先看一个更简单的变体:
1. char args => args is char.
2. char *args => args is pointer to char.
3. char *args = NULL => args is pointer to char, pointing to NULL.
4. char *args = malloc(9); => args is pointer to char, pointing to some address.
returned by malloc().
即:args
在所有步骤中都有一个地址和任何变量一样。你总是可以说:
printf("Address of args=%p\n", (void*)&args);
上面的2.,3。和4.之间的区别是
1。除非它是静态的,全局的,否则它将为NULL。
现在我们可以查看char **args = NULL
。
1. char **args;
2. char **args = NULL;
3. char **args = malloc(1);
4. char **args = malloc(1);
args[0] = malloc(9);
Address 1. args: 0xabc00 /-> Nowhere /-> Nowhere 2. args: 0xabc00 -> NULL /-> Nowhere 3. args: 0xabc00 -> 0xcf324 /-> Nowhere 4. args: 0xabc00 -> 0xcf324 -> 0xedf00 - 0xedf09
现在,如果你想要一些函数来操纵 args 指向你对该函数的贡献?您需要传递 args 指向的地址。
如果您还希望更改其指向的位置以及指向的内容,该怎么办?您需要传递指针本身的地址。这样你就可以说:&#34; args指向这里而不是那里。&#34;
再次:当您在 args 上执行 realloc()时,您需要使用新地址更新 args 以指向< / em>的
我很难理解如何处理这个三维数组以及如何动态分配它。
&#34;三维数组&#34; 。它是指针到指针到指针。或者根据上下文可能更简单:它是指向双指针的指针。或者如果你愿意,可以指向二维数组。
char **args = NULL; | +-----> 0x123400 -> NULL parse_cmdline(&args, cmdline); | +-----> 0x123400 -> NULL
在 parse_cmdline():
中 int parse_cmdline(char ***argv, const char* cmd)
&argv => Address of the argument.
argv => 0x123400 (Same address as in main)
*argv => NULL (Points to NULL)
/* Inside loop: */
/* First round: */
*argv = realloc(*argv, (i + 1) * sizeof(char*));
| |
| +--------> Initial address NULL
+------------------------> Updated address 0x1234af
/* Second round */
*argv = realloc(*argv, (i + 1) * sizeof(char*));
| |
| +--------> Old address 0x1234af
+------------------------> Updated address 0x1234bd
/* ... */
返回主 args 仍然有地址0x123400
,但它不再指向NULL但是最后 realloc()的地址,例如{{1 }}
现在第二个问题是如何正确访问数组的元素,假设你有一个指向指针变量的指针,而不是一个指针指针变量。
0x1234bd
vs ptr->args
在主功能中,您可以通过以下方式访问 args :
args
等
然而在 parse_cmdline()中有一些怪癖。首先看一下:
printf("%s\n", args[0]);
printf("%s\n", args[1]);
您可以使用*argv
取消引用 argv 从main获取 args 。到现在为止还挺好。但是你说:
*
这会变坏,为了理解原因,你必须看C's Precedence Table。如您所见,*argv[i]
,数组下标的优先级高于[]
。因此,您索引的是 argv 而不是它指向的内容,那么您尝试取消引用索引的地址。类似的东西:
*
要解决此问题,请将解除引用封装到括号中并索引结果:
foo = argv[i];
*foo = malloc(...);
从简单的数学角度考虑它可能更容易:
(*argv)[i] = malloc(...);
/* and */
memcpy((*argv)[i], token, strlen(token) + 1);
您想要将12和4之和除以2.由于12 + 4 / 2
具有更高的优先级,您需要添加括号:
/
最后,如评论中所述,(12 + 4) / 2
没有给出字符串的长度。在您的代码中,它将为您提供char指针的大小。如:指针的大小。
sizeof
使用 strlen 等。你也可以制作自己的 strlen 用于教育目的。复制时,请记住也复制终止空字节。那就是:
char foo[32] = "ab";
int bar[64];
char *baz;
sizeof foo => 32
sizeof bar => 64 * size of int e.g. 64 * 8 = 512
sizeof baz => 8 (or 4 or what ever the size of a pointer is in
current environment.)
strtok()函数将分隔符替换为memcpy((*argv)[i], token, strlen(token) + 1);
|
+----- Include 0x00 at end of token.
,因此您无需手动添加分隔符。这也是你可以使用 strlen()的原因。除非你事先知道长度,否则不会终止null。
这是一个典型的主题,你只需要工作和工作,直到它下沉客栈。虽然是,但有更简单的方法来解决它,例如通过返回数组而不是通过ref更新它。等等,它具有教育意义,迫使你在一个层面上学习指针,论点等,让人们通过使它发挥作用来学习很多东西。
使用0x00
为您提供帮助。打印很多。打印字符串的长度,变量的大小等。在这种情况下,打印地址。例如在main中:
printf()
如果你遇到困难,请退后一步。尝试传递printf("args=%p\n", (void*)&args);
而不是args
并继续处理。还要尝试在函数中使用临时指针:
&args
答案 1 :(得分:0)
#include <stdio.h>
#include <stdlib.h>
int parse_cmdline(char ***argv, char *input);
int main(void){
char **args = NULL;
char cmdline[] = "cmdline -s 20 -r -t parameter -p 20 filename";
int count = parse_cmdline(&args, cmdline);
for(int i = 0;i<count;++i)
printf("%s\n", args[i]);
printf("\n");
return 0;
}
#include <string.h>
int parse_cmdline(char ***argv, char *input){
int i=0;
char *token=strtok(input," ");
while (token!=NULL) {
int len = strlen(token);
*argv=realloc(*argv,(i+1)*sizeof(char*));
(*argv)[i]=malloc(len+1);
strcpy((*argv)[i++],token);
token=strtok(NULL," ");
}
return i;
}