C语言基础及例程

输入输出函数

%d -  int

%ld – long int

%hd – 短整型

%c  -char

%f - float

%lf – double

%u – 无符号数

%x – 十六进制输出 int 或者long int 或者short int

%o - 八进制输出

%s – 字符串

Int len;

Scanf(“%d”,&len);

什么是指针

Int main(void)

{

         int* p; //p 是变量的名字,int * 是一个类型

         //这个变量存放的是int类型变量的地址。

         inti =3;

         p=&i;

         system(“pause”);

     return 0;

}

1.介绍内存,内存编号就是地址,内存分很多单元,每个单元对应一个编号. 介绍地址,内存单元的编号,

为什么4G内存只显示3G

从0开始的非负整数.

         0000~FFFF

         XP操作系统 为神马只能显示3G内存。

         常用的XP系统都是32位的系统,就是说在所有程序(包括)系统本身运行的时候,最多能使用2的32次方的地址,大家可以自己算一下2的32次方就是4G,但问题是,系统里面除了内存还有其它设备啊,显卡硬盘之类的都是需要地址的,所以,留给内存使用的地址只有3G多一点,剩下的要保留给其它设备。

指针就是地址. 热身小程序介绍指针

int * p;//p是变量的名字, int * 表示p变量的数据类型是存放int类型的地址的数据类型

//int * p; 不表示定义了一个名字叫做*p的变量

// int * p;应该这样理解: p是变量名, int *是数据类型,p变量的数据类型是int*

//所谓int * 类型 实际就是存放int变量地址的类型       

int i = 3;

p = &i;

/*1. p保存了i的地址, 因此p指向i

2. p不是i,i也不是p,修改p的值不影响i的值,修改i的值也不会影响p的值

3. 如果一个指针变量指向了某个普通变量, 则 *指针变量  就完全等同于  普通变量

                               例子:

                                               如果p是个指针变量,并且p存放了普通变量i的地址

                                               则p指向了普通变量i

                                               *p  就完全等同于  i

                                       在所有出现*p的地方都可以替换成i

                                      在所有出现i的地方都可以替换成*p

                                  *p 就是以p的内容为地址的变量*/

j =*p;  //等价于 j = i;

printf("i= %d, j = %d\n", i, j);

指针和指针变量的关系

l  指针就是地址,地址就是指针

l  地址就是内存单元的编号

l  指针变量是存放地址的变量

l  指针和指针变量是两个不同的概念

l  但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样

为什么使用指针

   指针的重要性

   直接访问硬件 (opengl显卡绘图)

   快速传递数据(指针表示地址)

   返回一个以上的值(返回一个数组或者结构体的指针)

         表示复杂的数据结构(结构体)

   方便处理字符串

   指针有助于理解面向对象

如何用基本类型的指针

Int * p

Double * p

指针常见错误

# include<stdio.h>

intmain(void)

{

         int * p;

         int i = 5;

         *p = i; //应该是p=&i

         printf("%d\n", *p);

         return 0;

}

例子2

int main(void)

{

         inti = 5;

         int* p;

         int* q;

         p= &i;

         //*q= p; //error 语法编译会出错

         //*q= *p;  //error

         p= q;  //q是垃圾值,q赋给p, p也变成垃圾值

         printf("%d\n",*q);   //13行

                   /*

                            q的空间是属于本程序的,所以本程序可以读写q的内容,

                            但是如果q内部是垃圾值,则本程序不能读写*q的内容

                            因为此时*q所代表的内存单元的控制权限并没有分配给本程序

                            所以本程序运行到13行时就会立即出错

                   */

         return0;

}

介绍一下野指针. 蓝屏

错误3:

int main(void)

{

         int* p;//等价于int *p;int* p;

         inti=5;

         charch = A;

         //错误一 char ch ='A'

         //错误二 p=&ch;

         return0;

}

互换两个数字

检测实参形参是不是同一个参数

经典指针程序交换两个数组

#include <stdio.h>

int main(void)

{

         inta =3;

         intb =5;

         swap(&a,&b);

         printf("a=%d,b=%d",a,b);

         return0;

}

void swap(int a, int b){  //数值传递,没有变

         intt;

         t= a;

         a= b;

         b= t;

}

void swap(int * p ,int * q){  //引用传递,改变了

         intt;

         t=*p;

         *p= *q;

         *q= t;

}

//C++中用&表示引用传递,效果和指针传递一样,还更简洁。
void swap(int &x, int &y)
{
   int temp;
   temp = x; /* 保存地址 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y  */
  
   return;
}

*号的三种含义

1.乘法3*5

         2.定义指针变量 int *  p;//定义了一个名字叫p的变量,能够存放int数据类型的地址

         3.指针运算符,

         //如果p是一个已经定义好的指针变量

         则*p表示以p的内容为地址的变量

函数返回一个以上的值

void f(int *p ,int *q)

{ *p =1;   //指针重新指到1;

 *q=2;

}

Int main(void)

{ int a =3,b=5;

f(&a,&b);

Printf(“%d %d \n”,a,b);   // 结果a=1,b=2

Return 0;

}

通过被掉函数修改主调函数普通变量的值

1.实参必须是普通变量的地址

2.形参必须是指针变量

3.被掉函数中通过修改 *形参名的方式修改主调函数相关变量的值

指针和数组的关系

数组名,下标和指针的关系,指针变量的运算

数组名

int a[5] //a是数组名,5是数组的大小,元素个数

int a[3][4] // 3行4列 a[0][0]就是数组的第一个元素

Int b[5]

A=b ;//错误

一维数组名是个指针常量,它存放的是一维数组第一个元素的地址

int a[5];

inta[3][4];

printf("%#X\n",&a[0]);

printf("%#X\n",&a);

下标和指针的关系

如果p是个指针变量则

         p[i]等价于 *(p+i)

确定一个一维数组

确定数组需要两个参数及其原因

# include <stdio.h>

//f函数可以输出任何一个一维数组的内容

void f(int * pArr, int len)

{

         inti;

         for(i=0; i<len; ++i)

                   printf("%d  ", *(pArr+i) );  //*pArr *(pArr+1) *(pArr+2)

         printf("\n");

}

int main(void)

{

         inta[5] = {1,2,3,4,5};

         intb[6] = {-1,-2,-3,4,5,-6};

         intc[100] = {1, 99, 22, 33};

         f(a,5);  //a是 int *

         f(b,6);

         f(c,100);

         return0;

}

/*     一定要明白 pArr[3]和a[3] 是同一个变量

*/

# include <stdio.h>

void f(int * pArr, int len)

{

         pArr[3]= 88;

}

int main(void)

{

         int a[6] = {1,2,3,4,5,6};

        

         printf("%d\n", a[3]);

         f(a, 6);

         printf("%d\n", a[3]);

         return0;

}

  

指针的运算

# include<stdio.h>

int main(void)

{

         int i = 5;

         int j = 10;

         int * p = &i;

         int * q = &j;

         int a[5];

         p = &a[1];

         q = &a[4];

         printf("p和q所指向的单元相隔%d个单元\n", q-p);

         //p - q 没有实际意义

         return 0;

}

指针占用几个字节

# include<stdio.h>

int main(void)

{

         char ch = 'A';

         int i = 99;

         double x = 66.6;

         char * p = &ch;

         int * q = &i;

         double * r = &x;

         printf("%d %d %d\n",sizeof(p), sizeof(q), sizeof(r));

         return 0;

}

动态内存分配问题

       传统数组的缺点

                   1.数组长度必须实现指定,

                   并且只能是常整数.

                   inta[5];

                   intlen;

                   inta[len];//error

                   2.传统形式的数组,程序员没法手动释放空间

         数组一旦定义,系统为该数组分配的空间一直存在

         函数运行完毕,数组的空间就被释放

                   3.数组的长度不能在函数运行中动态增加或者缩小

                   4.A函数定义的数组只有在A没执行完毕前被使用,a函数运行完毕后,a的数组就无法被其他函数使用

#include<stdio.h>

void g(int * arr){

         arr[2]=2;

}

void f(){

         int a[] ={1,2,3,4,5};

         g(a);

         printf("%d",a[2]);

}

int main(){

         f();

}

创建动态数组

Malloc 函数 

realloc  re- allocate

# include<stdio.h>

# include<malloc.h>  //不能省  malloc是 memory(内存) allocate(分配)的缩写

int main(void)

{

         int i = 5; //分配了4个字节 静态分配   11行

         int * p = (int *)malloc(4); //12行

                   /*

                            1. 要使用malloc函数,必须添加malloc.h这个头文件

                            2. malloc函数只有一个形参,并且形参是整型

                            3. 4表示请求系统为本程序分配4个字节

                            4. malloc函数只能返回第一个字节的地址

                            5. 12行分配了8个字节, p变量占4个字节, p所指向的内存也占4个字节

                            6. p本身所占的内存是静态分配的, p所指向的内存是动态分配的         

                   */

         *p = 5; //*p 代表的就是一个int变量, 只不过*p这个整型变量的内存分配方式和11行的i变量的分配方式不同

         free(p); //freep(p)表示把p所指向的内存给释放掉  p本身的内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放

         printf("大家好!\n");

         return 0;

}

用主函数外部方法修改主函数申请的一块内存中的值

补充写void f() 函数

#include<stdio.h>

#include <malloc.h>

void f(int * p)

{ *p = 20;

    // free(p);     //free(p) 会对第二次打印*p产生什么影响

 }

int main(void)

{ int i =5;

    int * p = (int *) malloc(sizeof(int));

    *p = 10;

    printf("%d\n",*p);

   

    f(p);

    printf("%d\n",*p);

    system("pause");

   

}

问 free(q) 能不能在 f()里面写

静态内存和动态内存的比较

动态创建一个动态大小的数组,赋值打印,释放空间

#include<stdio.h>

#include<malloc.h>

int main(void)

{

 int * pArr;

 printf("请输入你要存放的元素的个数:");

 int len;

 scanf ("%d",&len);

 pArr=(int*)malloc(sizeof(int)*len);

  for(int i= 0; i<len;i++)

         {  scanf("%d",&pArr[i];

         }

 for(int i= 0; i<len;i++)

         {

              printf("%d\n",*(pArr+i));

         }

 system("pause");

 return 0;

}

静态内存和动态内存

静态内存是系统是程序编译执行后系统自动分配,由系统自动释放,静态内存是栈分配的.动态内存是堆分配的.

(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多.

  堆和栈的区别:

1.申请方式

栈:   

由系统自动分配.例如,声明一个局部变量int  b; 系统自动在栈中为b开辟空间.例如当在调用涵数时,需要保存的变量,最明显的是在递归调用时,要系统自动分配一个栈的空间,后进先出的,而后又由系统释放这个空间. 

堆:   

需要程序员自己申请,并指明大小,在c中用malloc函数   

如char* p1  =  (char*) malloc(10);   //14byte

但是注意p1本身是在栈中的.

  2  申请后系统的响应   

 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。   

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,    会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。   

3.申请大小的限制   

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(vc编译选项中可以设置,其实就是一个STACK参数,缺省2M),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。   

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。   

 4.申请效率的比较:   

栈:由系统自动分配,速度较快。但程序员是无法控制的。   

堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.   

 5.堆和栈中的存储内容   

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。   

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。   

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。   

6.内存的回收

栈上分配的内存,编译器会自动收回;堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。

  堆和栈的区别可以用如下的比喻来看出:   

使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。   

使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

静态变量不能跨函数使用

# include<stdio.h>

void f(int ** q)//q是个指针变量,无论q是什么类型的指针变量,都只占4个字节

{

         int i = 5;

         //*q等价于p  q和**q都不等价于p

         //*q = i; //error 因为*q = i; 等价于 p = i; 这样写是错误的

         *q = &i;  // p = &i;

}

int main(void)

{

         int *p; //13行

        

         f(&p);

         printf("%d\n", *p);  //16行  本语句语法没有问题,但逻辑上有问题

         return 0;

}

多级指针

用来存指针变量的地址

int main(void)

{

         int i=10;

         int * p=&i;

         int ** q= &p;

         int *** r = &q;

把i打印出来

         return 0;

}

函数的指针

1.定义int (*pf)(int x, int y);

2.赋值 pf = add;

3.引用 pf(3,5);

#include<stdio.h>

#include<stdlib.h>

int max(int a, intb)

{

return a>b ? a:b;

}    

main(){

    int a=10;

    int b=11;

   

    int(*pfun)(int x,int y);

    pfun=max;

   

    printf("%d\n", pfun(a,b));

    

    system("pause");

}      

  

结构体

#include<stdio.h>

#include<stdlib.h>

void study(){

     printf("do something");

}

//定义一个结构体

struct student{

       int age;

       int height;

       char sex;

      

       //结构体中不能定义函数,但可以定义函数指针;

       void (*studyP)();

}

main(){

       //定义结构体的变量

       struct  student  st = {20, 180, 'm', study};

       printf("%d\n", st.age);

       printf("结构体的长度%d\n", sizeof(st));

      

       //调用函数指针的三种写法

       st.studyP();//变量调用

      

       struct student* stp = &st;

       (*stp).studyP();//指针调用

      

       stp->studyP();//指针调用

       system("pause");

}

第一种

struct Student{ 

int age;

  float score;

}

第二种

struct Student2{

 body

} st2;

第三种

struc{

body

} st3

联合体

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

#include<stdio.h>

main( )

 {

struct date { intyear, month, day; }today;

union { long i;int k; char ii; } mix;

printf("date:%d\n",sizeof(structdate));

printf("mix:%d\n",sizeof(mix));

}

mix.I mix .kmix.ii共用相同的地址

#include<stdio.h>

main()

 {

      struct date { int year, month, day;}today;

      union { long i; int k; char ii; double d;} mix;

     printf("date:%d\n",sizeof(struct date));

      printf("mix:%d\n",sizeof(mix));

      mix.i = 33;

      mix.ii = 'a';

      printf("i=%d\n",mix.i);

      //110100000101

      system("pause");

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值