在 C 中创建数组

2023-11-24

我正在尝试用 C 创建一个 UNIX shell。如果是用 Java,那将是小菜一碟,但我对 C 没有那么丰富的经验。C 中的数组让我有点困惑。我不确定如何声明或访问某些数据结构。

我想创建一个字符串来在每一行中读取。很简单:只是一个字符数组。我将按如下方式初始化它:

char line[256]; //Maximum size of each line is 255 characters

要访问该数组的元素,我将执行以下操作:

line[0] = 'a'; //Sets element 0 to 'a'
fgets( line, sizeof line, stdin ); //Gets a line from stdin and places it in line

以这种方式声明和使用字符串与将其声明为指针有何不同?根据我的理解,C 中的数组会衰减为指针。那么,下面的内容是等价的吗?

char *line = (char*) malloc( sizeof(char) * 256 );
line[0] = 'a';
fgets( *line, sizeof(line), stdin );

什么时候使用指针字符“*”,什么时候不使用?在上面的示例中,在 fgets 中包含“*”是必要的还是正确的?

现在,我想创建一个字符串数组,或者更确切地说,创建一个指向字符串的指针数组。我会这样做吗?

char *arr[20]; // Declares an array of strings with 20 elements

我该如何访问它?

arr[0] = "hello" // Sets element zero of arr to "hello"

它是否正确?

我如何将此数组传递给函数?

execvp("ls", arr); // Executes ls with argument vector arr

这是正确的吗,或者我会使用指针 *arr 吗?如果是这样,为什么?

现在更糟糕的是,我想要一个字符串数组的数组(例如,如果我想保存多个参数向量,以便按管道顺序执行多个命令)。 是否会声明如下?

char **vector_arr[20]; // An array of arrays of strings

我将如何访问该数组的元素?

execvp("ls", vector_arr[0]); // Executes ls with first element of vector_arr as argument vector

我认为我对指针是什么,甚至数组与指针的关系有了很好的理解,但是我似乎无法将其与实际代码联系起来。我猜想在处理指针时,我不知道何时引用 *var、var 或 &var。


让我们来谈谈表达式 and types因为它们与 C 中的数组相关。

Arrays

当你声明一个数组时

char line[256];

the 表达 line具有类型“256 元素数组char"; 除非该表达式是sizeof或一元&运算符,它将被转换(“衰减”)为“指向的指针”类型的表达式char",表达式的值将是数组第一个元素的地址。鉴于上述声明,以下所有条件均成立:

 Expression             Type            Decays to            Equivalent value
 ----------             ----            ---------            ----------------
       line             char [256]      char *               &line[0]
      &line             char (*)[256]   n/a                  &line[0]
      *line             char            n/a                  line[0]
    line[i]             char            n/a                  n/a
   &line[0]             char *          n/a                  n/a
sizeof line             size_t          n/a                  Total number of bytes 
                                                               in array (256)

注意表达式line, &line, and &line[0]全部产生相同的结果value(数组第一个元素的地址与数组本身的地址相同),只是类型不同。在表达式中&line,数组表达式是操作数&运算符,因此上面的转换规则不适用;而不是指向的指针char,我们得到一个指向 256 个元素的数组的指针char。类型很重要;如果你写下类似下面的内容:

char line[256];
char *linep = line;
char (*linearrp)[256] = &line;

printf( "linep    + 1 = %p\n", (void *) (linep + 1) );
printf( "linearrp + 1 = %p\n", (void *) (linearrp + 1) );

每行都会得到不同的输出;linep + 1会给出下一个的地址char下列的line, while linearrp + 1会给出下一个的地址256 个元素的数组char下列的line.

表达方式line不是一个可修改的左值;你不能分配给它,所以像

char temp[256];
...
line = temp;

将是非法的。没有为变量预留存储空间line从.....分离line[0]通过line[256];没有什么可分配的to.

因此,当您将数组表达式传递给函数时,函数接收到的是指针值,而不是数组。在函数参数声明的上下文中,T a[N] and T a[]被解释为T *a;三人均声明a作为指向T。参数的“数组性”在调用过程中已丢失。

所有数组访问都是通过指针算术完成的;表达方式a[i]被评估为*(a + i)。数组表达式a首先按照上面的规则转换为指针类型的表达式,然后我们偏移i elements从该地址并取消引用结果。

与 Java 不同,C 不会为指向数组的指针与数组元素本身分开留出存储空间:所留出的全部内容如下:

+---+
|   | line[0]
+---+
|   | line[1]
+---+
 ...
+---+
|   | line[255]
+---+

C 也不从堆中为数组分配内存(无论堆的定义如何)。如果声明了数组auto(也就是说,对于一个块来说是本地的并且没有static关键字),内存将从实现为局部变量获取内存的地方(我们大多数人称之为堆栈)分配。如果数组是在文件范围内声明的或使用static关键字,内存将从不同的内存段分配,并且它将在程序启动时分配并保留到程序终止。

与 Java 不同的是,C 数组不包含有关其长度的元数据; C 假设您在分配数组时知道该数组有多大,因此您可以自己跟踪该信息。

Pointers

当你声明一个pointer like

char *line;

表达方式line具有类型“指向char“(废话)。留出足够的存储空间来存储a的地址char目的。除非您在文件范围或使用static关键字,它不会被初始化,并且将包含一些随机位模式,这些位模式可能对应也可能不对应于有效地址。鉴于上述声明,以下所有内容均正确:

 Expression             Type            Decays to            Equivalent value
 ----------             ----            ---------            ----------------
       line             char *          n/a                  n/a
      &line             char **         n/a                  n/a
      *line             char            n/a                  line[0]
    line[i]             char            n/a                  n/a
   &line[0]             char *          n/a                  n/a
sizeof line             size_t          n/a                  Total number of bytes
                                                               in a char pointer
                                                               (anywhere from 2 to
                                                               8 depending on the
                                                               platform)

在这种情况下,line and &line确实给了我们不同的值,以及不同的类型;line是一个简单的标量对象,所以&line给我们该对象的地址。同样,数组访问是根据指针算术完成的,因此line[i]无论 line 被声明为数组还是指针,其工作方式都是相同的。

所以当你写的时候

char *line = malloc( sizeof *line * 256 ); // note no cast, sizeof expression

这是像 Java 一样工作的情况;您有一个单独的指针变量,它引用从堆分配的存储,如下所示:

+---+ 
|   | line -------+
+---+             |
 ...              |
+---+             |
|   | line[0] <---+
+---+
|   | line[1]
+---+
 ...
+---+
|   | line[255]
+---+

与 Java 不同,当没有更多引用时,C 不会自动回收该内存。当你完成它时,你必须使用free库函数:

free( line );

至于你的具体问题:

fgets( *line, sizeof(line), stdin );

When do you use the pointer character '*', and when don't you? In the example above, is including the '*' in fgets necessary, or correct?

这是不正确的;fgets期望第一个参数的类型为“指向char"; the 表达 *line有类型char。声明如下:

char *line; 

第二,sizeof(line)只给你的大小pointer,不是指针所指的大小to;除非你想准确地阅读sizeof (char *)字节,您必须使用不同的表达式来指定要读取的字符数:

fgets( line, 256, stdin );
Now, I would like to create an array of strings, or rather, an array of pointers which point to strings. Would I do so as follows?
char *arr[20]; // Declares an array of strings with 20 elements

C 不像 C++ 或 Java 那样有单独的“字符串”数据类型;在C中,一个string只是一个以 0 结尾的字符值序列。它们是stored作为数组char。请注意,上面声明的只是一个包含 20 个元素的指针数组char;这些指针可以指向不是字符串的东西。

如果所有字符串都具有相同的最大长度,则可以声明一个二维数组char像这样:

char arr[NUM_STRINGS][MAX_STRING_LENGTH + 1]; // +1 for 0 terminator

然后你将每个字符串分配为

strcpy( arr[i], "some string" );
strcpy( arr[j], some_other_variable );
strncpy( arr[k], MAX_STRING_LENGTH, another_string_variable );

尽管提防strncpy;如果源字符串比目标字符串长,它不会自动将 0 终止符附加到目标字符串。在尝试将终止符与字符串库的其余部分一起使用之前,您必须确保终止符存在。

如果要为每个字符串单独分配空间,可以声明指针数组,然后分配每个指针:

char *arr[NUM_STRINGS];
...
arr[i] = malloc( strlen("some string") + 1 );
strcpy( arr[i], "some string" );
...
arr[j] = strdup( "some string" ); // not available in all implementations, calls
                                  // malloc under the hood
...
arr[k] = "some string";  // arr[k] contains the address of the *string literal*
                         // "some string"; note that you may not modify the contents
                         // of a string literal (the behavior is undefined), so 
                         // arr[k] should not be used as an argument to any function
                         // that tries to modify the input parameter.

请注意,每个元素arr是一个指针值;这些指针是否指向strings(0 终止序列char)或不取决于你。

Now even worse, I would like an array of arrays of strings (for example, if I wanted to hold multiple argument vectors, in order to execute multiple commands in pipe sequence). Would it be declared as follows?
char **vector_arr[20]; // An array of arrays of strings

您声明的是一个指向 char 指针的指针数组;请注意,如果您不知道有多少个指向的指针,则这是完全有效的char您需要存储在每个元素中。但是,如果您知道每个元素的最大参数数量,那么编写可能会更清楚

char *vector_arr[20][N];

否则,你必须分配每个数组char *动态地:

char **vector_arr[20] = { NULL }; // initialize all the pointers to NULL

for ( i = 0; i < 20; i++ )
{
  // the type of the expression vector_arr is 20-element array of char **, so
  // the type of the expression vector_arr[i] is char **, so
  // the type of the expression *vector_arr[i] is char *, so
  // the type of the expression vector[i][j] is char *, so
  // the type of the expression *vector_arr[i][j] is char

  vector_arr[i] = malloc( sizeof *vector_arr[i] * num_args_for_this_element );
  if ( vector_arr[i] )
  {
    for ( j = 0; j < num_args_for_this_element )
    {
      vector_arr[i][j] = malloc( sizeof *vector_arr[i][j] * (size_of_this_element + 1) );
      // assign the argument
      strcpy( vector_arr[i][j], argument_for_this_element );
    }
  }
}

所以,每个元素vector_arr是指向 M 元素数组的指针的 N​​ 元素数组char.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C 中创建数组 的相关文章

随机推荐

  • 模拟框架如何工作?

    如果我要编写一个模拟库 它将如何工作 换句话说 它们如何工作 我想知道的一件事是 您总是设置期望 因此实际上您需要将期望与该方法在运行时执行的操作进行比较 因此我假设需要反射 在运行时解析类型 另外 当使用术语 模拟对象 时 该对象是被删除
  • 如何将键映射到数据框列的多个值?

    我有一个 df 列 如下所示 col1 Non Profit Other 501c3 501c3 Sole Proprietor 如何创建字典对象或映射层 对所有建议开放 如果它匹配条件并更改键值 我可以在其中传递任何值 例如 如果该值为O
  • asp.net MVC - ValidationSummary 不显示

    我有一个奇怪的问题 即未显示 ValidationSummary 但是 正在显示 ValidationMessage 我检查了输出页面源代码 它们的颜色并没有遮挡它们 我用的是RC 有任何想法吗 编辑 在 ValidationSummary
  • [Pandas]如何获取每组中前n%的记录

    这是我的数据框 df pd DataFrame 1 A 40 2 A 60 3 A 47 4 B 33 5 B 69 6 B 22 7 B 90 8 C 31 9 C 78 10 C 12 11 C 89 12 C 88 13 C 99 c
  • 将当前日期增加 5 天

    date date Y m d current time timestamp 0 我该如何改变 date to date 5 days PHP 版本是 5 2 此代码不起作用 date cur date Y m d current time
  • 为什么 std::apply 使用通用函数会失败?

    取自参考参数 为什么调用std apply add generic 编译失败 有办法解决吗 include
  • 如何正确卸载 C++ Shell 扩展 DLL

    I have shell 扩展 dll用 C 和 COM 编写 dll 被注册并加载到内存中 我的升级设置程序将执行以下操作 注销shell扩展dll 杀死explorer exe 复制更高版本的 shell 扩展 dll 步骤 2 启动e
  • 如何设置默认 Elixir 混合任务

    如果我有一个mix exs文件类似 defmodule Mix Tasks My task do use Mix Task shortdoc Perform my task def run do IO puts Working end en
  • Android:检测导航栏可见性

    如何检测导航栏的存在并将其隐藏 In my onCreate I call hideNavigationBar 方法来隐藏导航栏 然后我注册一个侦听器 以便在用户触摸屏幕上的任何位置时每次导航栏变得可见时隐藏导航栏 如文档 当导航栏在触摸事
  • javascript排序函数排序错误[重复]

    这个问题在这里已经有答案了 你好 我有一个文本框 其值如下
  • 如何在http标头中设置用户代理

    我正在尝试在 C 中的重写函数 GetWebRequest 中的 http 标头中设置用户代理 protected override System Net WebRequest GetWebRequest Uri uri System Ne
  • `git push origin`的结果是什么?

    我在我的本地功能分支 foo 上工作 然后我想将这个新分支推送到原点 以便其他人可以对其进行处理 执行此操作的正常方法是 git push origin foo foo 我最终做到了 它完全按照预期工作 推起了 61 个物体 在此之前 我一
  • 抓取“元素周期表”和所有链接的维基页面

    我想抓取以下维基文章 http en wikipedia org wiki Periodic table 这样我的 R 代码的输出将是一个包含以下列的表格 化学元素简称 化学元素全名 化学元素维基页面的 URL 显然 每个化学元素都有一行
  • Django:“模块”对象没有属性“索引”

    过去几天我一直在尝试学习 Django 但最近我偶然发现了一个我似乎无法解决的问题 在完成 Django 自己的关于编写第一个应用程序的教程后 我决定再次阅读它 直到现在 我才会替换所有内容 以满足我正在构建的原始应用程序的要求 所以 一切
  • spring-boot 应用程序的嵌入式 Tomcat 目录列表

    我有一个带有嵌入式 Tomcat 的 Spring Boot 应用程序 我想通过 tomcat 目录列表从不同位置公开一些图像文件和文件夹 所以我在我的配置文件中添加了以下内容 public class AppConfig extends
  • 在 iOS 设备上运行网络服务器

    我想创建一个可以在其他 iOS 设备之间共享文件的应用程序 也可以在桌面计算机之间共享文件 所以我想做的是在我的设备上运行一个轻量级 Web 服务器 允许从设备下载文件 如下所示 http myphone local 1234 folder
  • 如何在 C# 中证明方法永远不会返回 null(按合同设计)

    我有一个永远不会返回空对象的方法 我想澄清一下 这样我的 API 的用户就不必编写如下代码 if Getxyz null do stuff 我该如何表达这个意图 不幸的是 没有办法内置到 C 中 您可以记录这一事实 但这不会被自动检查 如果
  • 桌面版 flutter,Visual Studio 2022 问题

    当我尝试在 Windows 上运行 flutter 应用程序时收到此消息 Building Windows application CMake Error at CMakeLists txt 2 project Generator Visu
  • Couchdb数据库-每用户设计文档管理

    我正在评估 Couchdb 作为一个有很多用户的 Web 应用程序 如果我使用每用户数据库模型 想必每个数据库中的设计文档都是相同的 我没有发现任何提及作用于多个数据库的 全局 设计文档 或任何可以轻松更改所有用户数据库的设计文档的工具 这
  • 在 C 中创建数组

    我正在尝试用 C 创建一个 UNIX shell 如果是用 Java 那将是小菜一碟 但我对 C 没有那么丰富的经验 C 中的数组让我有点困惑 我不确定如何声明或访问某些数据结构 我想创建一个字符串来在每一行中读取 很简单 只是一个字符数组