C语言学习

指针就是地址,地址就是指针;
地址就是内存单元的编号;
指针变量是以地址为内容的变量;

1基本类型指针

起步

#include <stdio.h>
int main(){
  int *p;
  int i=0;
  p=&i;
/* 对 p=&i的理解:保存了i的地址,也称p指向i;
   p不是i,i也不是p,修改i的值,p不变,修改p的值,i不变;
   指针变量指向了一个变量,那么(*指针变量)完全等同于(那个变量);
   即在代码中  *p 完全等同于 i ;*/
   return 0;
}

野指针问题

#include <stdio.h>

int main (){
  int *p;
  int *q;
  int i=0;
  p=&i; //p指向了i;
  p=q;//q没有具体指向,是一个野指针,此时p被赋值,也成了野指针;
  printf("%d,%q",*p,*q)//当p,q是野指针的时候,*p,*q 操作是不被允许的;
/*因为q在声明之后没有赋值,因此q的值是一个垃圾值
因此,*q此时代表的是以这个垃圾值为地址的一个变量
当前程序大概率并没有读取此变量的权限,程序报错*/
  return 0;
}

互换两个变量的值问题
朴素的想法

#include <stdio.h>

 void swap(int a,int b){
  int t=0;
  t=a;
  a=b;
  b=t;
  printf("a=%d,b=%d\n",a,b);
}
int main(){
  int a=3,b=5;
  swap(a,b);
  printf("a=%d,b=%d",a,b);
  return 0;
}

不朴素但不行
不行因为是意图更改变量的地址

#include <stdio.h>

void swap(int * p,int * q){
  int  * t;
  t=p;
  p=q;
  q=t;
}

int main(){
  int a=3,b=5;
  swap(&a,&b);
  printf("a=%d,b=%d",a,b);
  return 0;
}

#include <stdio.h>

void swap(int * p,int * q){//定义了两个int * 型号的指针变量p,q;
  //p接收了a的地址,q接收了a的地址
  int  t;
  t=*p;  //*p 读作:p这个地址下的值
  *p=*q;//读作:以p的内容为地址的变量=以q的内容为地址的变量的值;
  *q=t;
}

int main(){
  int a=3,b=5;
  swap(&a,&b);//变量a的地址给了p,变量b的地址给了q;
  printf("a=%d,b=%d",a,b);
  return 0;
}

通过被调函数来更改主函数普通变量的值的一般步骤:
1.将要修改的变量的地址作为实参传递给函数;
2.函数的形参要为指针变量;
3.在函数内以(*变量= xxx)的形式修改变量;

2指针和数组

指针和一维数组
一维数组名是一个指针常量

#include <stdio.h>

int main()
{
	int a[5];
	int b[5];
   
	//a=b;//error,因为a,b均为常量;
	
	printf("%#x \n",&a[0]);
	printf("%#x",a);
	
	return 0;
}

一位数组指针与下标
如果p是一个指针变量,则p[ i ]完全等价于*( p + i);

通过函数来处理数组

#include <stdio.h>
//要通过一个函数来处理一个数组,应当传递数组首元素地址以及数组长度
void f(int * parr,int len){
	for(int i= 0;i<len;i++){
	printf("%d ",*(parr+i));//这里p为指针变量,此时*(p+i)=p[i]
	//上面的*(parr+i)完全可以写做parr[i]
	}
	printf("\n");	
}
int main()
{
	int a[5]={1,2,3,4,5};
	int b[10]={2,2,2,2,2,2,2,2};
    
	f(a,5);
	f(b,10);
	
	return 0;
}

指针变量的运算
指针变量的加乘除没有意义
当两个指针指向同一片内存区域时,相减有意义
指针变量的大小
和系统控制内存的地址线数量有关,数据的地址和数据本身没有任何关系,比如说 int a=10,a这个变量值为10,但是其地址却是一个和计算机处理器位数密切相关的值,如果是32位系统,那么其地址就是由这32根总线组合的某一个32位二进制数字串,八位是一个字节,那么32位就是4个字节,相对应的,64位系统中就是8个字节。

动态内存分配
传统形式定义的数组长度固定,不能扩容,开辟的内存空间不能被手动释放,且不能被跨函数调用(当数组所在函数执行完被销毁后)

动态数组的创建

malloc函数的简单使用和理解

#include <stdio.h>
#include<malloc.h>
int main()
{
    int i = 5;  //静态分配了4个字节
	int * p = (int *) malloc(4);
	/*
		1.4代表malloc为本程序动态分配了4个字节
		2.malloc函数会返回分配的内存空间的首字节的地址
		因此需要有一个(int *)来声明这片地址是为了整形而开辟
		即当(int *)malloc(100)时,系统会自动以4个字节为一个整体读取这一片数据
		3.这行代码一共开辟了4+sizeof(p)个字节,p本身的内存是静态分配
		p指向的内存是动态分配
		4.此时p指向了malloc函数开辟的前sizeof(int)个字节 
	*/
	
	*p = 5;//合法,上述代码实现的*p本质上就是一个int变量
	
	free(p);
	/*free(p)表示把p指向的内存释放,p本身为静态分配,只能由系统释放*/
   printf("Hello, World! \n");
   
   return 0;
}

创建动态的一维数组
动态扩建或者缩小内存: realloc函数;

	#include <stdio.h>
#include <malloc.h>
int main()
{
	int * parr;
	int len;
	printf("输入数组的长度:");
	scanf("%d",&len);
	
	parr = (int*)malloc(4*len);//类似于int parr[len]

	parr[1] = 23;
	printf("%d",parr[1]);
			
   return 0;
}

静态内存和动态内存的比较
静态内存由系统创建和释放,在系统的栈里面分配
动态内存可由程序创建和释放,在系统中的堆里分配

3指针和函数

静态变量不能跨函数调用
动态可以

4指针和结构体

如何定义结构体 (3 种方式)

#include <stdio.h>

struct student 
{
	int age;
	double score;
	char sex;
};
int main()
{
	struct student st1={1,2,'d'};//声明,赋值
 	printf("%d %lf %c",st1.age,st1.score,st1.sex);
   return 0;
}

结构体的赋值和初始化

struct Student 
{
int age;
..
}

struct Student st1 = {3,...}//第一种
struct Student st2;//第二种
st2.age = 3;

结构体内部成员的取出

  1. 用.符号
  2. 指针变量名->成员名
    struct Student st = {.....};
	struct Student * pst = &st;
	pst->age;

*pst->age 在计算机内部被转化成(pst).age后执行
*所以pst->age等价于(pst).age等价于st.age
pst->age的含义:
pst所指向的那个结构体变量中的age这个成员;

结构体变量和结构体指针作为函数参数传递的问题

#include <stdio.h>
#include <string.h>
struct student 
{
	int age;
	double score;
	char name[10];
};

void InputStudent(struct student * pstu){
	pstu->age = 12;
	pstu->score = 99;
	strcpy(pstu->name,"embed");
}

//当函数不需要改变参数的值时,形参可以不用指针,此处s科完全看做st1的拷贝
void OutputStudent(struct student s){
	printf("%d  %f  %c", s.age , s.score , s.name );
}
//当然,用指针更加节省空间
void OutputStudent1(struct student *s){
	printf("%d  %f  %c", (*s).age , (*s).score , (*s).name );
}
int main()
{
	struct student st1;
	
	InputStudent( &st1 );
	
	OutputStudent( st1 );
	
	OutputStudent1( &st1 );
	
   return 0;
}

结构体变量的运算
不能加减乘除,只能相互赋值

实现一下冒泡排序

#include <stdio.h>

void sort(int *a, int len)
{
	int i,j,t;
	for(i = 0;i < len-1;i++)
		{
		  for (j = 0;j < len-1-i; j++)
		  {
			if(a[j]>a[j+1])
			{
			  t = a[j];
			  a[j] = a[j+1];
			  a[j+1] = t;
			}
		  }
		}
}

int main()
{
	int a[6] = {6,5,4,3,2,1};
	
	sort(a,6);
	
	for(int i = 0;i < 6;i++){
		printf("%d",a[i]);
	}
   return 0;
}

结构体的综合运用,学生信息管理

5多级指针

多级指针

#include <stdio.h>
#include <malloc.h>
int main()
{
	int i = 10;
	int * p = & i; // p是一个int型指针变量,只能指向int类型数据数据
	int ** q = & p;// q是一个 int* 型指针变量,只能指向int*类型的数据
	int *** r = & q;// r是一个 int** 型指针变量,只能指向int**类型的数据
	//***r 等价于 变量i
	// r = &p //error,类型不匹配
	return 0;
}

如果p是 int* 类型,那么&p是int**类型,注意跨函数调用的形参写法;

#include <stdio.h>

void f (int ** q){
	//本函数内 *q == p
}

void g(){
	int i=10;
	int * p = &i;
}

int main()
{
	g();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值