C语言知识总结(3)
数组
数组的特点:
只能存放一种类型的数据,比如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
指针与结构体
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)的更多相关文章
- 【转】R语言知识体系概览
摘要:R语言的知识体系并非语法这么简单,如果都不了R的全貌,何谈学好R语言呢.本文将展示介绍R语言的知识体系结构,并告诉读者如何才能高效地学习R语言. 最近遇到很多的程序员都想转行到数据分析,于是就开 ...
- STM32F4 阿波罗 库函数与C语言知识
先聊一聊: 之前使用32都是用的库函数,但是没有理解为什么那么操作,有很多的文件我也不知道要看哪一个,感觉云里雾里,没有学清楚一件东西的感觉不太好,于是就在前几天一直跟着比较详细的视频学习.开始老师讲 ...
- C语言知识汇总,史上最全面总结,没有之一
C语言基础 C语言学习路线 C语言入门笔记 初识C语言 简单的C程序示例 我们编写的C代码是怎样跑起来的? 简单示例,VS2019调试C语言程序 C语言基础-数据类型 深入理解变量,变量的声明,定义, ...
- 老师不讲的C语言知识
老师不讲的C语言知识 导语: 对于工科生,C语言是一门必修课.标准C(ANSI C)这个看似简单的语言在硬件底层编程.嵌入式开发领域还是稳坐头把交椅.在20年5月份,C语言就凭借其在医疗设备上的广泛应 ...
- Go语言知识查漏补缺|基本数据类型
前言 学习Go半年之后,我决定重新开始阅读<The Go Programing Language>,对书中涉及重点进行全面讲解,这是Go语言知识查漏补缺系列的文章第二篇,前一篇文章则对应书 ...
- 关于C语言知识调查
因为上一篇随笔对这一部分写得不够清楚,因此在这篇做一些补充. 你是怎么学习C语言的? 起初,对于C语言的学习主要是通过老师课堂的教学,完成相关的课后作业.与我的技能相比的话,他们都有一个共同点需要去实 ...
- 基础语言知识JAVA
1. 总结: JAVA比较重要的博客: http://www.runoob.com/java/java-tutorial.html (JAVA教程) http://blog.csdn.net/ ...
- [Java面试九]脚本语言知识总结.
核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery ...
- JavaScript语言知识收藏
接触Web开发也已经有一段时间了,对javascript的认识也比以前有了更加深入的认识了,所以觉得应该整理一下. 一.JavaScript不支持函数(方法)的重载,用一个例子证明如下: functi ...
- C语言知识整理(3):内存管理(详细版)
在计算机系统,特别是嵌入式系统中,内存资源是非常有限的.尤其对于移动端开发者来说,硬件资源的限制使得其在程序设计中首要考虑的问题就是如何有效地管理内存资源.本文是作者在学习C语言内存管理的过程中做的一 ...
随机推荐
- openldap 安装 配置 使用
1.安装 #安装 yum install -y openldap-servers openldap-clients openldap-devel 2.复制配置文件 #复制配置文件 cp /usr/sh ...
- 约瑟夫圆环的C++实现
转载请注明出处:点我 昨天参加了企鹅的2015年实习生招聘的笔试,编程题第一道题就是约瑟夫圆环问题,要求用C++来实现. 约瑟夫圆环问题其实是一个很有名的问题:问题的描述为: 设有编号为1,2,……, ...
- 放肆的使用UIBezierPath和CAShapeLayer画各种图形
CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形,当然,你也可以使用其他方式来画,随你. 杂谈 在 CAShapeLayer 中,也可以像 CAL ...
- oc-16-set,get方法
S.h #import <Foundation/Foundation.h> /** 解决方案: 1.不用@public修饰 2.我们对象有访问和设置成员变量的两种操作 1>设置值 p ...
- 适用于各浏览器支持图片预览,无刷新异步上传js插件
文件上传无疑是web应用中一个非常常用的功能,不管是PHP.jsp还是aspx.mvc等都会需要文件上传,但是众所周知当使用自带的文件上传功能时总会出现页面刷新的情况.当然现在有了html5这个好东西 ...
- php调试工具总结
一:XDebug+Webgrind 二:XHProf
- c++ 设计模式9 (Abstract Factory 抽象工厂模式)
5.2 抽象工厂模式 动机:在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作. 代码示例: 实现利用数据库的业务逻辑,支持多数据库(Sq ...
- 客户端动态化系列之——Weex
来源:kuailejim 链接:http://www.jianshu.com/p/ea25fd834944 客户端动态化系列之--URLRoute 在前端越来越火的年代,逐渐衍生出类似React Na ...
- SQL Server 表和索引存储结构
在上一篇文章中,我们介绍了SQL Server数据文件的页面类型,系统通过96个字节的头部信息和系统表从逻辑层面上将表的存储结构管理起来,具体到表的存储结构上,SQL Server引入对象.分区.堆或 ...
- java笔记 chapter1 java是什么,能干什么,有什么,特点,开发环境
一,java是什么 二,java能干什么 三,java有什么 四,java的特点 五,java的三大特性:虚拟机,垃圾回收和代码安全 六,构建JSE开发环境:下载安装jdk和配置环境变量 七,编写并运 ...