数组

数组的特点:

  • 只能存放一种类型的数据,比如int类型的数组、float类型的数组

  • 里面存放的数据称为“元素”

初始化方式

int a[] = {, , };

int a[] = {,};

int a[] = {, , };

int a[] = {[]=,[] = };

常见错误

int a[];

int[] a;

int a[b];

a = {, };

a[] = {,,,}; 

内存分析

  • 数组存储空间的大小

  • 存储空间的划分(内存的分配是从高地址到低地址进行的,但一个数组内部元素又是从低到高进行的)

  • 数组名的作用,查看元素地址
  • 数组越界的注意

二维数组

二维数组是一个特殊的一维数组:它的元素是一维数组。例如int a[2][3]可以看作由一维数组a[0]和一维数组a[1]组成,这两个一维数组都包含了3个int类型的元素

初始化

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

int a[3][4] = {{},{},{}};

int a[][5] = {3,21,31,2,32,1};

注意错误:

int a[3][4];

a[3] = {};

字符串

很多个字符组合在一起就是字符串了

初始化

  • char a[] = “123”;  和 char a [] = {‘1’,’2’,’3’};的区别,可以比较大小

  • “123”其实是由’1’、’2’、’3’、’\0’组成

  • “123”的存储分布

  • 字符串的输出”%s”,’\0’是不会输出的

\0的作用

'\0'是一般字符串语句中的结束符号,可以判断字符串是否结束

字符串数组

初始化

char names[][] = { {'J','a','y','\0'}, {'J','i','m','\0'} };
char names2[][] = { {"Jay"}, {"Jim"} };
char names3[][] = { "Jay", "Jim" };

指针

定义格式    类名标识符 *指针变量名;    如  int *p;

先定义后赋值

// 简单取值
int a = ;
int *p;
p = &a;
printf(“%d”, *p);
// 简单改值
*p = ;

定义的同时赋值

int a = ;
int *p = &a;

清空指针

p = ;
p = NULL;

指针与数组

1、指向数组元素的指针与普通指针变量一样,例如

int a[];
int *p=&a[];
p=a; //数组名代表数组的首地址
p=&a[];//指针指向第二个元素

在数组里面,通过*(p+1)、*(p-1)就可以直接获取上下元素。
2、指针下标:在C语言中,中括号[]是下标运算符,一般我们习惯用数组名[下标]对数组元素进行访问,实际上,下标运算符可用于指针运算,例如:

int a[];
int *p=&a[]; //指针p指向数组的第二个元素

p[1] --> *(p+1) 下一个元素,即a[2]
p[-1] --> *(p-1) 上一个元素,即a[0]

下标运算符:D[N]没有规定D和N的顺序,N[D]也是可以的,例如:
1[p] --> *(1+p)
(p+1)[1] --> *(p+1+1) //用的人少
3、访问数组元素的4种方式:  a[i]、 *(a+i)、p[i]、*(p+i)

#include <stdio.h>
int main(){
int x[] = {,,};
int s=,i,*p=x+; //*p = &x[2]
for(i=;i>=;--i)
s -= (-i)[p]; // (-i)[p] = *(-i+p) 1-1=0 0-2=-2 -2-3=-5
printf("%d\n",s); // -5
return ; //1[p] --> *(1+p)
}

 指针与常量

1、指向常量的指针:指针变量指向一个常量,此时指针所指向的值不能修改,但指针本身(存储的地址)可以指向另外的对象,例如

int a = , b = ;
const int *p = &a;
*p = ; //报错,不能修改
p = &b; //可以修改

2、指针常量:声明指针常量,则指针本身不能改变。例如

int a=, b=;
int *const p = &a;
p = &b; //报错,不能修改
*p = ; //可以修改

理解:看const后面是什么,如果是*p则表示指针的值不能修改,如果是p则表示指针不能更换指向。
3、类型转换:C语言中,如果将常量的地址赋值给非常量指针(类型转换),就可以修改地址里的值,从而影响代码中用到常量的表达式。例如

const int a=;  //常量
int *p = (int*)&a; //类型换换,去掉const
*p = ; //修改地址指向的值
int b = a+; //101

指针与字符串 

1、字符串除了用字符数组处理外,还可以用字符指针来处理,字符指针指向字符串常量(连续存储区)的首地址。例如

char str[] = "Hello";
const char *pstr = "Hello";

注意:"Hello"字符串常量为 const char* ,所以字符指针定义也要用const char*。(C语言中也可以只用 char*)
2、赋值:可以将字符串整体赋值给字符指针,而字符数组除了初始化的时候是不能整体赋值的,如

char str[];
const char *pstr;
str = "Hello"; //错误
pstr = "Hello"; //正确

理解:字符数组名是一个常量,不能被赋值,而字符指针是一个变量,存储字符串常量的地址,变量的值(存储的地址)是可以改变的。 
3、访问字符元素:访问字符串的字符元素,通过下标或指针移位的方式即可,和普通数组一样。如

const char *p = "Hello";
p[]; //'e'
p[] = 'A'; //报错,不能修改字符串常量的元素
char a[]="Hello", *pa=a;
pa[] = 'A'; //可以修改

注:要理解字符指针指向的是字符数组还是字符串常量。

数组指针

1、数组指针:指向一个数组的指针变量(注意不是指向数组的首元素)称为数组指针(也称行指针)。例如

int a[];
int *p1=a; //指向数组首元素的指针,指向的类型是int
int (*p2)[]=&a; //数组指针,指针指向的类型是4个int组成的数组
p1+ --> a[]
p2+ --> a[] //注意这里+1是4个int的大小

注意:赋值要使用p2 = &a,如果用p2=a会报错,因为a的类型是 int *,  而&a的类型是int (*)[4],这是两种指针类型(虽然地址一样)。
2、访问数组元素:由于数组指针指向一个数组,那么*p就是数组本身,(*p)[i]表示数组的第i个元素,例如

int a[]={,,,};
int (*p)[]=&a;
(*p)[] -->

数组指针与二维数组

1、数组指针用于二维数组,指针指向以二维数组每一行数组,例如
int a[3][4];
//可看成3个int [4]的数组:a[0]、a[1]、a[2]
int (*p)[4]=a; //定义指向int [4]数组的指针变量
p --> a[0]
p+1 --> a[1]
注意:二维数组名a为int (*)[4]类型。
2、访问数组元素,如
p[0][1] --> a[0][1]
*(*(p+1)+2) --> a[1][2]
演示-用数组指针遍历输出二维数组{{1,2,3,4},{5,6,7},{8,9}}。
注意:p[i][j]  -- > *(*(p+i) + j)

#include <stdio.h>
int main(){
int a[][]={{,,,},{,,},{,}};
int i,j,(*p)[]=a;
for(i=;i<;++i){
for(j=;j<;++j)
//printf("%d ",p[i][j]);
printf("%d ",*(*(p+i)+j));
printf("\n");
}
return ;
}

指针数组与二维数组

1、指针数组:指定义一个由指针组成的数组,即数组中的每个元素都是指针变量,例如

const char *p[];
//由p[0]..p[3]组成的数组,每个都是char*指针

2、指针数组用于二维数组,下标表示二维数组的行数,例如:

int a[][];
//可看成3个int [4]的数组:a[0]、a[1]、a[2]
int *p[]; //定义3个数组
p[] = a[]; //指针赋值
p[] = a[];

3、数组元素:由于p[i]的类型是int*数组,其元素可以用 +j ,或下标[j]来访问,如
p[0][1]  --> a[0][1]
*(p[0]+1) --> a[0][1]
演示-用指针数组遍历输出二维数组{{1,2,3,4},{5,6,7},{8,9}}。
注:p[i][j]  -- > *(p[i] + j)

#include <stdio.h>
int main(){
int a[][]={{,,,},{,,},{,}};
int i, j, *p[];
p[] = a[];
p[] = a[];
p[] = a[];
for(i=;i<;++i){
for(j=;j<;++j)
//printf("%d ",p[i][j]);
printf("%d ",*(p[i]+j));
printf("\n");
}
return ;
}

指针数组与字符串

指针数组适用于指向若干字符串(每个字符串不定长,用二维字符数组很浪费空间),会使字符串处理更加灵活,效率更高。例如
const char *p[3]={"Hello","new world","how are you"};
p[0][1] --> 'e'
*(p[0]+1) --> 'e'   
注:每一个字符串指针都是以'\0'作为结尾。

指向指针的指针

1、指针变量*p存储的是一个地址,它自己也有地址,可以再用一个指针变量*prt指向p的地址,prt称之为指向指针的指针变量,简称二级指针,例如  int i, *p=&i, **prt=&p; 注意此时指针类型为“指向整型数据的指针变量”,不是整型数据。 prt = &i; //错误,指针类型不符
2、对指针数组*p[n]来说,数组名代表首地址,每一个元素都是指针型数据,因此它就是一个二级指针,也可以用一个二级指针进行赋值及运算,例如
int a[4]={1,3,5,7};
int *pa[4]={&a[0],&a[1],&a[2],&a[3]};
int **p=pa;
**p --> 1
**(p+1) --> 3

指针与结构体

1、结构体类型的指针变量,定义方式为
struct 结构体类型 *变量名;
struct student stu, *p;
p = &stu;
2、成员的引用:访问成员有以下三种方式:
stu.name;
(*p).name;
p->name; //指针专用,指向运算符,优先级比单目运算符高
p->age++; //先获得age成员,再++
练习-根据姓名查询学分

#include <stdio.h>
#include <string.h>
struct students{
int no;
char *name;
int score;
};
struct students *find(struct students *s, int len, const char *name)
{
while(strcmp(s->name , name) != )
s+=;
return s;
}
int main()
{
struct students stu[] = {{, "张三", },
{, "李四", },
{, "张思", },
{, "李武", },
{, "张武", }};
struct students *a = find(&stu[], , "李武");
printf("输出信息为,姓名:%s,学号:%d,成绩:%d", (*a).name, (*a).no, (*a).score);
return ;
}

指针作为函数参数

函数参数是指针变量时,传递的是指针存储的地址,而变量做参数时传递的是具体的值(会产生形参的拷贝)。指针做参数时,地址(指针自己的值)不能被修改,但地址指向的值可以被修改。例如

void change(int *p){
*p = ;
}
int main(){
int a=;
change(&a); //传入指针,在函数里a的值被修改
}

数组作为函数参数 

数组名可作为函数的实参和形参,传递的是数组的首地址,在函数被调用时,对形参指向的值的修改会使得实参数组发生变化。由于传递的是首地址,那么也可以用指针来表示形参或实参,效果相同。
1、形参实参都用数组名:

void f(int arr[],int len){...}
int main(){
int a[];
f(a, );
}

2、实参用数组名,形参用指针变量

void f(int *x,int len){...}
int main(){
int a[];
f(a, );
}

3、实参用指针变量,形参用数组名:

void f(int arr[],int len){...}
int main(){
int a[],*p=a;
f(p, );
}

4、实参形参都用指针变量:

void f(int *x,int len){...}
int main(){
int a[],*p=a;
f(p, );
}

演示-设计函数cat,将第二个参数的字符串被连接到第一个字符串后面。

#include <stdio.h>
void cat(char *p1, const char *p2){
while(*p1) *p1++; //到p1的'\0'位置,
while(*p2)
*p1++ = *p2++; //将p2复制给p1的'\0'后面位置
*p1 = '\0'; //最后加个'\0'
}
int main(){
char s1[]="Hello"; //长度要够
const char *s2 = " world!";
cat(s1,s2);
printf("%s\n",s1);
return ;
}

指针型函数

返回值类型为指针的函数称之为指针型函数,如
int *num(int x, int y);
类型 *函数名([形参列表]);
注意:返回指针类型意味着不需要生成拷贝,效率较高。
理解:*的优先级低于()的优先级,因此函数名先和后面的()结合,以上格式意味着首先是一个函数,然后返回值是一个指针。如果是以下格式:int (*num)(int x, int y)
则先将*和num结合,表示num是一个指针变量,指向一个函数(称为函数指针)。

函数指针

1、指向函数的指针变量称之为函数指针。例如
int (*func)(int x, int y);
类型 (*函数名)([形参列表]);
函数指针变量指向一个函数的地址,可以将同样原型的函数赋值给变量,例如

int max(int x,int y);
int (*p)(int x,int y);
p = max; //将函数赋值给指针变量

定义了函数指针变量以后,就可以用此变量来调用函数,也可以将之作为函数的参数(回调函数)。
2、调用函数:用函数指针变量调用max和min函数。

int max(int x, int y);  //求最大值
int min(int x, int y); //求最小值
int (*p)(int x, int y);
p=max;
printf("%d\n",p(,)); //
p=min;
printf("%d\n",p(,)); //

3、回调函数:又称callback,是一种事件驱动的编程模式,将函数指针变量作为参数传递给另外一个函数,函数里面当某些事件满足时会调用此函数指针变量指向的函数,例如。

void print(int n){  //回调函数,打印信息
printf("回调函数调用:%d\n",n);
}
void loop(int max, int (*p)(int n)){
//遍历1..n,如果7的倍数就打印信息
int i;
for(i=;i<max;++i){
if(i%==) p(i);
}
}

理解:诸葛亮交给赵云三个锦囊,如果发生**事情,就打开第几个锦囊,这里的锦囊就是传递进去的函数指针变量。
4、自定义函数指针类型:可以使用typedef来自定义一种函数指针类型,例如

typedef (*MYFUNC)(int n); //自定义类型PRINT_FUNC
MYFUNC p = print; //将print函数赋值给变量p
p(); //调用函数指针变量

动态分配内存

1、用malloc函数可以在程序中动态分配内存空间,返回void*指针,这时需要用一个指针变量将首地址保存起来,以后可以使用,如果没有保存,则这块空间就丢失了(称之为内存泄漏),例如

int *p = (int*)malloc(sizeof(int));
*p = ;
p = (int*)malloc(sizeof(int));
//p指向新地址,原内存丢失(泄漏)

注意:需要#include <malloc.h>。
2、动态数组:可以用malloc函数来定义动态数组。例如

int *p = (int*)malloc(sizeof(int)*);
//分配10个元素的数组
p[] = ;

3、释放内存:free函数用来将动态分配的内存空间还给系统,之后系统可以继续使用回收的内存。例如

int *p = (int*)malloc(sizeof(int));
free(p);
p = NULL;
int *p2 = (int*)malloc(sizeof(int)*);
free(p2);
p2 = NULL;

注意:指针变量被释放以后,不能再赋值,因为它没有内存空间,申请内存后才能赋值或运算。为了防止继续赋值导致崩溃,建议将指针设置为NULL。
4、栈和堆:malloc分配的内存空间从“堆”上申请内存,从低到高,而自动分配的如int a,是在“栈”上申请的内存空间,从高到低。

C语言知识总结(3)的更多相关文章

  1. 【转】R语言知识体系概览

    摘要:R语言的知识体系并非语法这么简单,如果都不了R的全貌,何谈学好R语言呢.本文将展示介绍R语言的知识体系结构,并告诉读者如何才能高效地学习R语言. 最近遇到很多的程序员都想转行到数据分析,于是就开 ...

  2. STM32F4 阿波罗 库函数与C语言知识

    先聊一聊: 之前使用32都是用的库函数,但是没有理解为什么那么操作,有很多的文件我也不知道要看哪一个,感觉云里雾里,没有学清楚一件东西的感觉不太好,于是就在前几天一直跟着比较详细的视频学习.开始老师讲 ...

  3. C语言知识汇总,史上最全面总结,没有之一

    C语言基础 C语言学习路线 C语言入门笔记 初识C语言 简单的C程序示例 我们编写的C代码是怎样跑起来的? 简单示例,VS2019调试C语言程序 C语言基础-数据类型 深入理解变量,变量的声明,定义, ...

  4. 老师不讲的C语言知识

    老师不讲的C语言知识 导语: 对于工科生,C语言是一门必修课.标准C(ANSI C)这个看似简单的语言在硬件底层编程.嵌入式开发领域还是稳坐头把交椅.在20年5月份,C语言就凭借其在医疗设备上的广泛应 ...

  5. Go语言知识查漏补缺|基本数据类型

    前言 学习Go半年之后,我决定重新开始阅读<The Go Programing Language>,对书中涉及重点进行全面讲解,这是Go语言知识查漏补缺系列的文章第二篇,前一篇文章则对应书 ...

  6. 关于C语言知识调查

    因为上一篇随笔对这一部分写得不够清楚,因此在这篇做一些补充. 你是怎么学习C语言的? 起初,对于C语言的学习主要是通过老师课堂的教学,完成相关的课后作业.与我的技能相比的话,他们都有一个共同点需要去实 ...

  7. 基础语言知识JAVA

    1. 总结: JAVA比较重要的博客: http://www.runoob.com/java/java-tutorial.html     (JAVA教程) http://blog.csdn.net/ ...

  8. [Java面试九]脚本语言知识总结.

    核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery ...

  9. JavaScript语言知识收藏

    接触Web开发也已经有一段时间了,对javascript的认识也比以前有了更加深入的认识了,所以觉得应该整理一下. 一.JavaScript不支持函数(方法)的重载,用一个例子证明如下: functi ...

  10. C语言知识整理(3):内存管理(详细版)

    在计算机系统,特别是嵌入式系统中,内存资源是非常有限的.尤其对于移动端开发者来说,硬件资源的限制使得其在程序设计中首要考虑的问题就是如何有效地管理内存资源.本文是作者在学习C语言内存管理的过程中做的一 ...

随机推荐

  1. android startActivityForResult的用法

    有时候我们需要把A activity提交数据给B  activity处理,然后把结果返回给A 这种方式在很多种情况需要用到,比如我应用的程序需要有拍照上传的功能. 一种解决方案是  我的应用程序 〉调 ...

  2. UITableViewController

    UITableViewController 目录 概述 UITableView UITableViewCell 与UITableViewController相关的代理 UITableViewDataS ...

  3. javascript 基本数据类型

    1. javascript 五种基本数据类型  Undefined Boolean Number String Null,Undefined 对应的值只有一个 undefined, Boolean 对 ...

  4. 深入解析 ext2 文件系统

     很久以来,就想写一篇关于ext 家族文件系统的文章,源于我刚工作的时候,曾经一不小心rm -rf,误删除了很多文件,当时真想有个数据恢复软件能帮我把数据回复了.当然学习数据恢复,首先要学习文件系统. ...

  5. Toad for Oracle 授权权限

    grant create session to 用户;//授予zhangsan用户创建session的权限,即登陆权限 grant dba to 用户; //授权dba权限,导入导出数据 grant ...

  6. 小白日记52:kali渗透测试之Web渗透-HTTPS攻击(Openssl、sslscan、sslyze、检查SSL的网站)

    HTTPS攻击 全站HTTPS正策划稿那位潮流趋势 如:百度.阿里 HTTPS的作用 CIA 解决的是信息传输过程中数据被篡改.窃取 [从中注入恶意代码,多为链路劫持] 加密:对称.非对称.单向 HT ...

  7. Java最重要的21个技术点和知识点

    (五)Java最重要的21个技术点和知识点之网络编程.泛型.编程规范相关 写这篇文章的目的是想总结一下自己这么多年JAVA培训的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享给刚 ...

  8. solr默认查询设置

    在搜索过程中,如果我们每次请求中都传入很多固定的参数,会很繁琐,这里再solrconfig.xml中初始化定义一些不经常改动的搜索参数: <requestHandler name="/ ...

  9. iOS- SQLite3的基本使用

    iOS- 简单说说iOS移动客户端SQLite3的基本使用  1.为什么要使用SQLite3?   •大量数据需要存储 •管理数据,存储数据   SQLite是一种关系型数据库(也是目前移动客户端的主 ...

  10. 基于 Equinox 的 OSGi Console 的研究和探索

    自定制 OSGi Console 进行组建和服务生命周期管理模块化编程的好处已经很好地被理解了约 40 年,但在 OSGi 之前,开发人员不得不自己发明模块化设计和系统.随着软件应用体系结构的不断发展 ...