总结了一些指针易出错的常见问题(四)

指针与结构体

  简介:我们可以使用C的结构体来表示数据结构元素,比如链表或树的节点,指针是把这些元素联系到一起的纽带。

typedef struct _person{
    char* firstName;
    char* lastName;
    char* title;
    unsigned int age;
} Person;/*用点表示法初始化*/

Person person;
person.firstName=(char*)malloc(strlen("Emily")+1);
stcpy(person.firstName,"Emily");
person.age=23;

/*****结构体指针初始化*/
Person *ptrperson;
ptrperson=(Person*)malloc(sizeof(Person));
ptrperson->firstName=(char*)malloc(strlen("Emily")+1);
strcpy(ptrperson->firstName,"Emily");
ptrperson->age=23;  // (*ptrperson).age = 23;

为结构体分配内存,分配的内存大小至少是各个字段的长度之和。不过,实际长度会大于这个和,结构体的各字段之间可能会有填充。结构体数组各元素之间会有填充。

结构体释放问题:

 用结构体变量和指向结构体的指针函数参数

  1.用结构体变量的成员作参数。(用法和普通变量相同)

  2.用结构体变量作实参。形参也必须是同类型的结构体变量。调用期间形参也要占用内存。(空间和时间上开销较大),较少使用该方法。

  3.用指向结构体变量(或数组)的指针作实参,将结构体变量(数组)的地址传给形参。

用指针处理链表

  链表是一种动态地进行存储分配的一种数据结构。 链表是C语言编程中常用的数据结构,比如我们要建一个整数链表,一般可能这么定义:

struct int_node {
        int val;
        struct int_node *next;
};

  假设有五个学生某一门功课的成绩分别为A、B、C、D和E,这五个数据在内存中的存储单元地址分别为1248、1488、1366、1022和1520,其链表结构如图所示。

  链表有一个“头指针”变量,图中以 head表示,它存放一个地址,该地址指向链表中第一个结点,第一个结点又指向第二个结点……直到最后一个结点。该结点不再指向其他结点,它称为“表尾”,它的地址部分存放一个“NULL”(表示“空地址”),链表到此结束。链表中每个结点都包括两个部分:用户需要用的实际数据和下一个结点的地址。

  链表每个结点中只有一个指向后继结点的指针,该链表称为单链表。其实结点中可以有不止一个用于链接其他结点的指针。如果每个结点中有两个用于链接其他结点的指针,一个指向前趋结点(称前趋指针),另一个指向后继结点(称后继指针),则构成双向链表。双向链表如图所示。

/*单链表的例子(静态链表:因为所有节点在程序中定义的,不是临时开辟的,也不能用完后释放)*/
#include<stdio.h>
//#define NULL 0

 struct student
 {
     int num;
     int score;
     struct student *Next;
 };
     int main()
     {
         struct student a,b,*head,*p;
         a.num=10012; a.score=99;
         b.num=10013; b.score=81;
         head=&a;
         a.Next=&b;
         b.Next=NULL;
         p=head;
         do
         {
             printf("%5d  %ld\n",p->num,p->score);
             p=p->Next;
         }while(p!=NULL);
     }

  问题:结构体的变量名可以当做地址赋给指针吗?没有头指针head行不行?p起了什么作用?没它可以吗?

处理动态链表用到的函数  calloc/malloc/free

  malloc函数原型:  void *malloc(unsigned int size);其作用是在内存的动态存储区分配一个长度为size的连续空间。此函数的值(返回值)是一个分配域的起始地址(void类型);若函数未成功执行,则返回空指针(NULL)。

  calloc函数原型:  void *calloc(unsigned n,unsigned size);其作用是在内存的动态存储区分配n个长度为size的连续空间。函数返回一个指向分配域起始位置的指针;否则,返回NULL。

  free  函数原型:  void free(void *p);释放由p指向的动态存储区。free无返回值。

建立动态链表(知难而进)

/*写一函数建立一个有3名学生数据的单向动态链表*/
 #include<stdio.h>
 #include<malloc.h>
 //#define NULL 0
 #define LEN sizeof(struct student)

 struct student
 {
     long num;
     float score;
     struct student *next;
  };
  int n;
  struct student *creat(void)
  {
      struct student *head;
      struct student *p1,*p2;
      n=0;
      p1=p2=(struct student*)malloc(LEN);
      scanf("%ld,%f",&p1->num,&p1->score);
      head=NULL;
      while(p1->num!=0)
      {
          n=n+1;
          if(n==1) head=p1;
          else
          p2->next=p1;
          p2=p1;
          p1=(struct student*)malloc(LEN);
          scanf("%ld,%f",&p1->num,&p1->score);
       }
       p2->next=NULL;
       return(head);
  }

 int main()
 {
     creat();
 }

建立链表首先要定义一个包含数据域和指针域的的结构类型,然后建立指向表头节点的头指针head,最后通过malloc函数动态申请一块内存作为表头节点。

typedef struct node
{
int data; /*信息*/
struct node *link; /*指针*/
}NODE; /*定义节点*/
NODE *head; /*定义头指针head */

定义结构类型和头节点之后,我们要建立不包含数据的表头节点,可以按下列语句进行操作。

NODE *p; /*说明一个指向节点的指针变量p */
p=(NODE*) malloc(sizeof(NODE)); /*申请表头节点*/
p->link = NULL; /*将表头节点的link置为NULL */
head=p; /*head指向表头节点p*/ 

由于此时链表中只有一个表头节点,没有数据节点,所以称为空链表。

p=(NODE*) malloc(sizeof(NODE)); /*申请一个数据节点*/  为了在链表中保存数据,可以从表头位置将数据节点插入到链表中,例如,插入一个数据节点:

p=(NODE*) malloc(sizeof(NODE)); /*申请一个数据节点*/
gets(p ->data); /*输入一个新的数据*/
p->link=head->link; /*建立链接关系。将表头节点的link存入p 的link中*/
head->link=p; /*将数据节点插在表头节点之后成为第一个数据节点*/ 

插入第一个数据节点后链表,然后继续插入下一个数据节点。

create(NODE *head,int n)  根据上面的链表建立过程,可以写出函数create建立有n个数据节点的链表。

create(NODE *head,int n)
{
NODE *p;
for(; n>0;n--)
{
p=(NODE*) malloc(sizeof(NODE));
if(p==NULL)
exit(0);
gets(p->data);
p->link = head->link;
head->link = p;
}
} 
时间: 2024-08-04 12:57:19

总结了一些指针易出错的常见问题(四)的相关文章

总结了一些指针易出错的常见问题(六)

安全问题与指针误用 声明和初始化指针(初始化失败) 误用指针 释放问题 指针声明: // 宏定义(仅仅是替换) #define PINT int*; PINT ptr1, ptr2;其实是定义 int* ptr1, ptr2;一个指针,一个整型常量. //typedef 命名已有数据类型(优于宏定义) typedef int* PINT; PINT ptr1, ptr2; 宏定义和typedef区别参考:浅谈c/c++typedef和#define区别 //  宏定义  和  typedef区别

总结了一些指针易出错的常见问题(三)

指针与字符串 NULL和NUL区别:前者用来表示特殊的指针((void*)0),而NUL是一个char(\0),不可混用. 字符常量:单引号:字符串:双引号: 字符串的声明方式:字面量.字符数组.指针. 字符串字面量池: 字符串初始化 初始化char数组:   char header[]="Media Player"; strcpy函数初始化数组 char header[13]; strcpy(header, "Meadia Player"); 2.初始化char指

总结了一些指针易出错的常见问题(七)

指针的其他重要内容 主要研究以下问题 指针的类型转换 访问硬件设备 别名和强别名 使用restrict关键字 线程(一是如何用指针在线程之间共享数据这个基本问题:二是如何用指针支持回调函数(一个操作可能会调用某函数来执行任务,如果实际被调用的函数发生了改变,我们称之为回调函数.)) 面向对象技术 转换指针 类型转换是一种基本操作,跟指针结合使用时很有用. 访问有特殊目的的地址: 分配一个地址来表示端口: 判断机器的字节数: 整数转换为整数指针: int num =8; int *pi = (in

总结了一些指针易出错的常见问题(二)

4.指针与数组  一些常见的错误观点是数组和指针是完全可以互换的.尽管数组名字有时候可以当指针来使用,但是数组的名字不是指针. 数组是能用索引访问的同质元素连续集合.(连续是指数组的元素在内存中是相邻的,中间不存在空隙同质是指元素是同一类型) 指针在处理数组时很有用,既可以用指针指向已有的数组,也可以从堆上分配内存然后把这块内存当做一个数组使用. 数组/指针表示法 给数组地址加1实际加了4,也就是整数的长度. #include<iostream> using namespace std; in

总结了一些指针易出错的常见问题(一)

1.认识指针 1.1 指针和内存 C程序在编译后,会以三种形式使用内存: 1)静态/全局内存 静态/全局声明的变量在这里.这些变量从程序开始运行时分配,直到程序终止才消失. 所有函数都能访问全局变量,静态变量的作用域则局限在定义它们的函数内部. 2)自动内存 这些变量在函数内部声明,并且在函数调用时才创建,他们的作用域局限于函数内部, 而且声明周期限制在函数的执行时间内. 3)动态内存 内存分配在堆上,可以根据需要释放,而且直到释放才消失.指针引用分配的内存,作用域 局限于引用内存的指针. 数组

mysql安装及易出错问题解决

.双击.msi 安装包 2.勾选同意,下一步 3.一直 下一步,遇到弹窗选择ok,然后点击Execute 4.check  ok后点击下一步 5.next 6.设置root密码,next 7.无脑next后,来到易出错界面了 - -! (启动服务失败--解决方案:1.卸载完全之前安装的mysql,可能有残留文件导致服务不能启动 2. MySQL安装是出现could not start the service mysql error:0 提示错误,解决方法:  首 先,在管理工具->服务里面将My

jQuery选择器与JavaScript易出错知识点

一.jQuery选择器 基本选择器 1.Id(根据给定的ID匹配一个元素.如果选择器中包含特殊字符,可以用两个斜杠转义.) jQuery代码为$("#myclass") 若有特殊字符则 HTML代码为<span id="myclass[1]" jQuery代码为$("#myclass\\[1\\]") 2.Class(一个用以搜索的类.一个元素可以有多个类,只要有一个符合就能被匹配到) jQuery代码为$(".myclass&q

如何给一个完全不懂的非程序员解释复杂的、耗时的、易出错的软件开发的过程?

简评:相当有趣的一篇文章,教导你如何给非程序员讲清楚最难.最复杂的那些开发问题. 回答这个问题首先你应该解释一下如何泡一壶茶: 烧水 把茶叶放在锅里 当水烧开后,把它倒入锅里 等待 5 分钟 把茶倒进杯子里 喝 现在开始变得有趣了.你需要开始问他们这些问题: 烧水? 水来自哪里? 水壶在哪里呢? 你怎么把水弄到水壶里去呢 你怎么知道放了多少水 如果没有水 / 水壶 / 电怎么办? 如果你的填充传感器失败了怎么办? 如果你的沸腾传感器失败了? 把茶叶放在锅里? 锅在哪里,如果没有一个呢?我们应该在

C提高_day03_一级指针易犯错误模型

1.char *(字符串)做函数参数出错模型分析 建立一个思想:是主调函数分配内存,还是被调用函数分配内存: //不要相信,主调函数给你传的内存空间,你可以写......一级指针你懂了. 但是二级指针,你就不一定懂...抛出......... void copy_str21(char *from, char *to) { if (*NULL = '\0' || *to!='\0') { Printf("func copy_str21() err\n"); return; } for (