gpt4 book ai didi

c - 传递指针变量以存储字符串数组(命令行参数)

转载 作者:太空宇宙 更新时间:2023-11-04 02:04:21 24 4
gpt4 key购买 nike

我的头撞在墙上已经好几个小时了,我需要你的帮助。在我的作业中,我应该编写一个函数,将字符串拆分成用空格分隔的标记。这些令牌被复制到动态分配的字符串数组中。字符串作为参数传递,第二个参数是指向字符串数组的指针变量(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.

我读了很多关于指针、字符串、数组和多维数组的文章,但是我似乎无法理解。有一件事我真的不明白,为什么指针是作为(&args)传递的,为什么不直接作为数组指针传递呢?我也不确定我是否正确使用了memcpy。

最佳答案

除了使用sizeof而不是strlen之类的错误用法之外,让我们先看看其他几个问题。
为什么&args
有一件事我真的不明白,为什么指针是作为(&args)传递的,为什么不直接作为数组指针传递呢?
看看你的陈述:

char **args = NULL;

这告诉你什么?
A.) 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.

B.) char **args = NULL;
它被初始化并指向空。在您的函数中,您将它指向的内容从NULL更新为realloc返回的新地址。
这里有一个非常重要的点:你要更新它指向的内容!如果不传递args的地址,就无法告诉main新位置在哪里。
你要处理一份通过的文件。
如果它指向某个地址,例如来自前一个malloc,您可以更新它指向的内容,但是您不能重新分配,因为您将无法更新指针本身。也就是说:回到主要位置,它仍然指向原来的位置。
你的目标是:
  args          *            *0x123e0a -> [0] 0x123fa00 -> 0x12fbae0 - 0x12fbae8 cmdline            [1] 0x123fa08 -> 0x12fbae9 - 0x12fbaec -s            [2] 0x123fa10 -> 0x12fcae0 - 0x12fcae3 20            [3] 0x123fa18 -> 0x12fbad8 - 0x12fbadb -r            ...

A simpler variant

If we look at a simpler variant first:

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。上面是
在第2点。变量没有指向任何内容。它未初始化。1
在第3点。它是初始化的,但是它不指向地址,而是空的。最后
在第4点。它有malloc返回的位置地址。
1。除非它是静态的、全局的等,否则它将为空。
现在我们可以看看 char **args = NULL
1. char **args;
2. char **args = NULL;
3. char **args = malloc(1);
4. char **args = malloc(1);
args[0] = malloc(9);

         Address1. args: 0xabc00 /-> Nowhere /-> Nowhere2. args: 0xabc00  ->    NULL   /-> Nowhere3. args: 0xabc00  ->  0xcf324 /-> Nowhere4. args: 0xabc00  ->  0xcf324  -> 0xedf00 - 0xedf09

Reallocating

Now, if you want some function to manipulate what ever args point to what would you give to that function? You need to pass the address of what args point to.

What if you also want to change where it points as well as what it points to? You need to pass the address of the pointer itself. This so you can say: "args point here instead of there."

  • Stage 1: ptr point to A
  • Stage 2: ptr point to B

Again: As you do realloc() on args you need to update args with the new address to point to.

I'm having a hard time to understand how to handle this triple dimensional array and how to dynamically allocate it.

It is not a "triple dimensional array". It is a pointer to a pointer to a pointer. Or perhaps simpler in light of context: it is a pointer to a double pointer. Or pointer to a two dimensional array if you like.

char **args = NULL;         |         +-----> 0x123400 -> NULL     parse_cmdline(&args, cmdline);                    |                    +-----> 0x123400 -> NULL

inside 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

/* ... */

后面的主参数仍然有address 0x123400,但它不再指向NULL,而是指向最后一个realloc()的地址,例如 0x1234bd
正在访问指向指针指针数组的指针。。。
现在,第二个问题是如何正确访问数组的元素,假设您有指向指针指针变量的指针,而不是指向指针变量的指针。
ptr->argsargs
在主函数中,可以通过以下方式访问args:
printf("%s\n", args[0]);
printf("%s\n", args[1]);

等。
但是在parse_cmdline()中有一些怪癖。首先看看这个:
*argv

在这里,您使用 *取消对argv的引用以从main获取args。到现在为止,一直都还不错。但是你说:
*argv[i]

这会变得很糟糕,要理解原因,您必须查看 C's Precedence Table。如您所见,数组下标的优先级高于 []。因此,您正在索引的是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 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.)

使用strlen等。你也可以为教育目的制作自己的strlen。当你复制时,记住也要复制终止的空字节。即:
memcpy((*argv)[i], token, strlen(token) + 1);
|
+----- Include 0x00 at end of token.

函数的作用是:用 sizeof替换分隔符,这样就不需要手动添加它了。这也是为什么可以使用strlen()的原因。如果不终止空值,除非事先知道长度,否则就没有可能。
努力工作吧
这是一个典型的话题,在这里你只需要努力工作直到它沉入酒店。虽然是的,但是有更简单的方法来解决它,例如返回数组而不是通过ref.等来更新它,这是有教育意义的,它迫使你学习指针、参数等,在某种程度上,一个人通过让它工作可以学到很多东西。
使用 0x00帮助您。打印很多。打印字符串长度、变量大小等,在这种情况下打印地址。例如在main中:
printf("args=%p\n", (void*)&args);

如果你被卡住了,就退后一步。试着通过 printf()而不是 args,并努力做到这一点。同时尝试在函数中使用临时指针:
int parse_cmdline(char ***argv, char *input)
{
int i = 0;
char **argx = *argv; /* Temporary local. */
char *token = strtok(input, " ");

fprintf(stderr, "argv=%p\n", (void*)argv);

while (token != NULL) {
fprintf(stderr,
"%15s, sizeof=%u strlen=%u\n",
token, sizeof(token), strlen(token)
);
/* Now you can refer argx like you would args in main().
* as in: argx[i] instead of (*argv)[i] etc.
*/
}
/* Update pointer before return. */
*argv = argx;
return i;
}

关于c - 传递指针变量以存储字符串数组(命令行参数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22570892/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com