LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2824|回复: 25

二维字符数组和一维字符指针数组有很大的不同吗?

[复制链接]
发表于 2005-3-22 09:08:31 | 显示全部楼层 |阅读模式
gtk_clist_append函数的原形是
  1. gint gtk_clist_append( GtkCList *clist,
  2. gchar *text[] );
复制代码

第二个参数是一维字符指针数组, 我为了方便就给它传了个二维字符数组, 结果出现段错误
改为传一维字符指针数组就一切ok了
发表于 2005-3-22 10:14:50 | 显示全部楼层
内存分布上应该是一样的吧。
比如一维char数组和二维char数组空间都是连续的。

gchar 是怎么定义的?
回复 支持 反对

使用道具 举报

发表于 2005-3-22 13:23:03 | 显示全部楼层
出错的code?
回复 支持 反对

使用道具 举报

发表于 2005-3-22 14:11:56 | 显示全部楼层
呵呵,又是指针和数组的区别。先看一段程序:
  1. int f(int *s[])
  2. {
  3.         return *s[0];
  4. }

  5. int main(void)
  6. {
  7.         int n1 = 7, n2 = 8;
  8.         int a[][1] = { 7, 8, };
  9.         int *b[] = { &n1, &n2, };

  10.         f(a);
  11.         f(b);

  12.         return 0;
  13. }

复制代码

再看它的汇编代码(为了适合阅读已经排版):
  1.         .file        "x.c"
  2.         .text
  3. .globl f
  4.         .type        f, @function
  5. f:
  6.         pushl        %ebp
  7.         movl        %esp, %ebp

  8.         movl        8(%ebp), %eax
  9.         movl        (%eax), %eax
  10.         movl        (%eax), %eax

  11.         popl        %ebp
  12.         ret
  13.         .size        f, .-f
  14. .globl main
  15.         .type        main, @function
  16. main:
  17.         pushl        %ebp
  18.         movl        %esp, %ebp
  19.         subl        $40, %esp
  20.         andl        $-16, %esp
  21.         movl        $0, %eax
  22.         subl        %eax, %esp

  23.         ; n1 = 7
  24.         movl        $7, -4(%ebp)

  25.         ; n2 = 8
  26.         movl        $8, -8(%ebp)

  27.         ; a[][1] = { 7, 8, }
  28.         movl        $7, -16(%ebp)
  29.         movl        $8, -12(%ebp)

  30.         ; b[] = { &n1, &n2, }
  31.         leal        -4(%ebp), %eax
  32.         movl        %eax, -24(%ebp)
  33.         leal        -8(%ebp), %eax
  34.         movl        %eax, -20(%ebp)

  35.         ; f(a)
  36.         leal        -16(%ebp), %eax
  37.         movl        %eax, (%esp)
  38.         call        f

  39.         ; f(b)
  40.         leal        -24(%ebp), %eax
  41.         movl        %eax, (%esp)
  42.         call        f

  43.         movl        $0, %eax
  44.         leave
  45.         ret
  46.         .size        main, .-main
  47.         .section        .note.GNU-stack,"",@progbits
  48.         .ident        "GCC: (GNU) 3.3.5  (Gentoo Linux 3.3.5-r1, ssp-3.3.2-3, pie-8.7.7.1)"
复制代码

可以看到,当用 a 做参数时,程序已经尝试访问(虚拟)地址为 7 的内存了。这当然会有段错误。

此外,全局的指针和数组不同,或者说,下面的声明和定义不是等价的:
  1. /* 第 1 个文件中 */
  2. int array[10];

  3. /* 另一个文件中 */
  4. extern int *array;
复制代码

原因也是类似的:请看一下编译器产生的汇编代码。
回复 支持 反对

使用道具 举报

发表于 2005-3-23 12:05:48 | 显示全部楼层
Post by nait
gtk_clist_append函数的原形是
  1. gint gtk_clist_append( GtkCList *clist,
  2. gchar *text[] );
复制代码

第二个参数是一维字符指针数组, 我为了方便就给它传了个二维字符数组, 结果出现段错误
改为传一维字符指针数组就一切ok了

二维字符数组的宽度是固定的,但指针数组的存放的指针可以指到任意长度的内存区。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-23 17:46:57 | 显示全部楼层
硬着头皮看了半天愣是没看懂那段汇编代码 :ask  :ask

下面是我的程序(原来的版本还有好多,被我精简掉了)
  1. /* Traverse through entire /proc and find out all process
  2. *if the entry consist of digits only and is dir itself,
  3. *it will be a process entry */
  4. #include <gtk/gtk.h>
  5. #include <dirent.h>
  6. #include <string.h>
  7. #include <ctype.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>


  10. int is_proc_entry(struct dirent *dirp)
  11. { // determine whether the entry is consist of digits or not
  12.   char *p = dirp->d_name;
  13.   int len = strlen (dirp->d_name);
  14.   while (p < dirp->d_name+len && isdigit(*p)) p++;
  15.   return (p == dirp->d_name + len);
  16. }

  17. void lsfiles()
  18. {
  19.   struct dirent *dirp;
  20.   DIR *dp;

  21.   dp = opendir ("/proc");

  22.   while ((dirp = readdir(dp)) != NULL)
  23.     if(is_proc_entry(dirp))
  24.       printf("%s\n", dirp->d_name);

  25.   closedir(dp);
  26. }



  27. GtkWidget *create_proc_list( void )
  28. {    // Todo: add process owner etc.

  29.   GtkWidget *scrolled_window;
  30.   GtkWidget *clist;
  31.   struct dirent *dirp;
  32.   DIR *dp;
  33.   gchar *title = "Current Process";
  34.   gchar buf_proc[1][20];

  35.   /* $(A44=(R;8vPB5D9v6/40?Z(B(scrolled window)$(A#,V;SPPhR*J1#,9v6/Lu2E3vOV(B */
  36.   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  37.   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
  38.                                   GTK_POLICY_AUTOMATIC,
  39.                                   GTK_POLICY_AUTOMATIC);
  40.   gtk_widget_set_size_request (scrolled_window, 100, 0);
  41.   gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 5);

  42.   clist = gtk_clist_new_with_titles (1, &title);
  43.   gtk_clist_set_shadow_type (GTK_CLIST(clist), GTK_SHADOW_OUT);
  44.   // set column with
  45.   gtk_clist_set_column_width (GTK_CLIST(clist), 0, 70);
  46.   gtk_clist_set_row_height (GTK_CLIST(clist), 25);

  47.   /* $(ATZ40?ZVPLm<SR;P)O{O"(B */
  48.   dp = opendir ("/proc");
  49.   while ((dirp = readdir(dp)) != NULL)
  50.     if(is_proc_entry(dirp)){
  51. [color=DarkRed]//如果象下面这样用二维字符数组就会出现段错误,但接下来的printf证明读进缓冲去的字符正是我所要求的[/color]
  52.       sprintf(buf_proc[0], "%s\n", dirp->d_name);
  53.       printf("%s", buf_proc[0]);
  54.       //gchar *msg = g_strdup_printf ("%s\n", dirp->d_name);
  55.       gtk_clist_append( GTK_CLIST(clist), buf_proc);
  56.       //g_free (msg);
  57.     }
  58.   closedir(dp);


  59.   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (scrolled_window), clist);
  60.   gtk_widget_show_all (scrolled_window);

  61.   return scrolled_window;
  62. }



  63. int main(int argc, char *argv[])
  64. {

  65.   GtkWidget *topwin;


  66.   gtk_init(&argc, &argv);

  67.   topwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  68.   gtk_window_set_title (GTK_WINDOW(topwin), "Process list");
  69.   gtk_window_set_resizable(GTK_WINDOW(topwin),FALSE);
  70.   g_signal_connect (G_OBJECT(topwin), "destroy", G_CALLBACK(gtk_main_quit),NULL);
  71.   gtk_container_set_border_width (GTK_CONTAINER (topwin), 10);

  72.   gtk_widget_set_size_request (topwin, 200, 305);

  73.   gtk_container_add (GTK_CONTAINER(topwin), create_proc_list());

  74.   gtk_widget_show(topwin);

  75.   gtk_main();
  76.   return 0;
  77. }
复制代码

实现的功能就是搜索/proc,把所有的进程找出来
回复 支持 反对

使用道具 举报

发表于 2005-3-23 18:11:50 | 显示全部楼层
那么,分析一下汇编代码的核心部分吧。
  1. movl        8(%ebp), %eax
  2. movl        (%eax), %eax
  3. movl        (%eax), %eax
复制代码

这来自函数 f,作用是把 *s[0] 放到寄存器 eax 中,这个寄存器用来保存函数的返回值。8(%ebp) 是参数 s 的值。(%eax) 表示用 eax 的值作为地址访问内存。这样,要访问 *s[0],一共需要两次间接访问:第一次是取得 s 的地址中的内容,也就是 s[0];然后是 s[0] 中的内容,也就是 *s[0]。如果这时传入的参数是个二维数组,第一步取到的就是这个数组的第一个元素的值。当然,这个值由函数声明中参数的类型决定。我们这个程序里全部使用了 int,所以就是 4 个字节,数值上是 7。下一步,就是把 7 作为地址取得内存中的数据。大多数操作系统里这个虚拟地址都是不可用的,所以会有段错误。如果恰好这个值非常大,并且它对应的物理地址正好属于当前进程的可用范围,就不会有段错误,但会有令人莫名其妙的错误。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-23 18:53:05 | 显示全部楼层
多谢楼上的大哥:)
我会慢慢看的
thx
回复 支持 反对

使用道具 举报

发表于 2005-3-23 21:47:24 | 显示全部楼层
Post by nait
硬着头皮看了半天愣是没看懂那段汇编代码 :ask  :ask


  1.   dp = opendir ("/proc");
  2.   while ((dirp = readdir(dp)) != NULL)
  3.     if(is_proc_entry(dirp)){
  4. [color=DarkRed]//如果象下面这样用二维字符数组就会出现段错误,但接下来的printf证明读进缓冲去的字符正是我所要求的[/color]
  5.       sprintf(buf_proc[0], "%s\n", dirp->d_name);
  6.       printf("%s", buf_proc[0]);
  7.       //gchar *msg = g_strdup_printf ("%s\n", dirp->d_name);
  8.       gtk_clist_append( GTK_CLIST(clist), buf_proc);
  9.       //g_free (msg);
  10.     }
  11.   closedir(dp);
  12.   
复制代码

实现的功能就是搜索/proc,把所有的进程找出来

段错误可能是数组下标越界造成的,用snprintf看看。
回复 支持 反对

使用道具 举报

发表于 2005-3-23 23:46:25 | 显示全部楼层
Post by kj501
段错误可能是数组下标越界造成的,用snprintf看看。

这里的段错误不是由于下标越界,而是由于楼主使用了错误的实参。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表