如果在程序定義了一個函數,在編譯時,編譯系統爲函數代碼分配一段存儲空間,這段存儲空間的起始地址,稱爲這個函數的指針
函數名就是函數開始的地址
函數二級指針可以修改函數指針的指向
函數指針,不僅僅是地址,必須明確函數指針類型和輸出參數類型和數量
函數名可以作爲參數傳遞給函數指針
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b)
{
return a + b;
}
void print()
{
printf("你好,明天\n");
}
int main()
{
printf("%p %p\n", add, print);
void(*p)() = print; //聲明函數指針,並初始化函數指針
int(*pa)(int a, int b) = add; //聲明函數指針,並初始化函數指針
//初始化指針只能傳遞地址
print(); //直接調用
p(); //調用函數指針,相當於調用函數,間接調用
printf("%d\n",pa(12,36));
system("pause");
}
函數的返回值可以是指針
#include <stdio.h>
#include <stdlib.h>
#include<time.h>
//函數,返回一個地址,對於數組而言,函數參數調用沒有副本機制
//函數返回值是一個指針類型的函數
int* mindata(int a[], int n) //查找最小數
{
int *p = NULL; //聲明指針變量,指向空指針,最後保存最小值的地址
int min = a[0];
p = &a[0];
for (int i = 1; i < n; i++) //選擇法
{
if (min > a[i])
{
min = a[i];
p = &a[i];
}
}
printf("最小的值爲%d\n",*p);
return p; //返回最小值的地址
}
int main()
{
int a[10];
time_t ts;
srand((unsigned int)time(&ts)); //按照時間設置隨機數種子
for (int i = 0; i < 10; i++)
{
a[i] = rand() % 100;
printf("%d\n",a[i]);
}
int *p = mindata(a,10); //獲取最小數的地址
//此時 直接修改*p 可以起到修改最小值 如,*p = 35;
printf("%d\n",p -a); //可以知道下標符號
system("pause");
}
左值,能放在賦值號左邊的值
int *const px; //指針常量
寫一個和strcpy函數功能相同的函數
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//寫一個和strcpy函數功能相同的函數
char* mystrcpy(char* dest, char* source) //兩個參數目的 ,來源
{
char * last = NULL; //最後結果
if (dest == NULL || source == NULL)
{
return last; //如果傳入的兩個字符串有一個是指向空指針的,結束函數
}
last = dest;//存入首地址
while ((*dest++ = *source++) != '\0');
//沒有遇到字符 '/0',一直拷貝數據
return last;
}
int main()
{
char str[40] = {0};
//char * p = mystrcpy(str, "你好明天\n"); //聲明一個指針接受字符串str的首地址
//printf("%p %p\n",str,p); 地址相同
//printf("%s", p);
//printf("地址爲%p\n", strcpy(str, "你好明天\n"));//strcpy函數返回字符串str的地址
printf("%s", strcpy(str, "山陰路的夏天\n")); //兩條語句的作用相同,函數沒有問題
printf("%s", mystrcpy(str, "你好明天\n")); //兩條語句的作用相同,函數沒有問題
system("pause");
}
指針包括 類型和地址,將地址轉換爲指針,需要進行類型的轉換
如:
int* p;
int x;
scanf("%x",&x);
p = (int*) x; //將地址轉換爲指針
void指針和空指針
void* 類型的指針,只包含地址不包含類型,是不指向任何類型的指針,任何類型都可以賦值給空類型指針,純地址的賦值,但指向不明確,大小不確定,無法取出內容,如果需要取讀地址的內容
應先對地址進行類型的轉換,一般用於參數還有返回值,不明確指針類型的情況傳遞地址,如 malloc() 函數的返回值
int* p =NULL;//不指向任何地址,即沒有初始化,空指針,*p等價於int類型,p等價於int*類型
void *p = malloc(20); //分配20個字節的內存,返回值賦值給void*類型指針變量p,可以轉換成任何類型的指針,malloc函數在stdlib.h頭文件中
int *px = (int*)p; //強制轉換指針類型,使分配的內存可以按照int類型解析
ferr(p); //根據地址釋放指針,此時p爲迷途指針
p = NULL;
動態內存分配
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//void* calloc(size_t num.size_t size)
//分配內存函數,第一個參數爲個數,第二個參數爲內存大小,內存自動初始化爲0
//void* realloc(void *ptr,size_t size)爲以分配的內存重新分配空間並複製內容。
//參數一,已分配的內存地址,參數二重分配的字節數 如果能拓展就拓展,不能就重新分配
int main()
{
int x;
scanf("%d",&x);
int *p = (int *)malloc( sizeof(int) * x ); //分配 x乘於int類型字節數的內存
//分配失敗則返回一個空指針NULL,返回值爲空類型指針 malloc(1024) == NULL 判斷是否分配成功
//指針變量p指向分配的內存之後,不應更改指針的指向
//實現動態數組,按照數組的方式訪問,
for (int *px = p,i = 0; px < p+x; i++,px++) //指針法
{
*px = i;
printf("%d\n",*px);
}
free(p); //根據地址釋放內存,一定要記得釋放內存,除非空指針能反覆釋放
p = NULL;
//內存釋放之後,指針應該賦值爲空,可以規避再次引用和反覆釋放的問題
//for (int i = 0; i < x; i++) //下標法
//{
// p[i] = i;
// printf("%d\n",p[i]);
//}
system("pause");
}
靜態內存分配由編譯器完成,如爲一個不大的數組分配內存
動態內存分配
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void detection(float* str)
{
for (int i = 0; i < 5; i++)
{
if (str[i] < 60)
printf("不及格成績爲%.2f 第%d個\n", str[i], i + 1);
}
}
int mainaa()
{
float* p = (float*)malloc(5 * sizeof(int));
printf("輸入五個成績並以空格隔開\n");
scanf("%f %f %f %f %f", p, p + 1, p + 2, p + 3, p + 4);
detection(p);
free(p); //釋放內存
system("pause");
}
return 也有副本機制,存儲在寄存器中,無法取地址
一級指針作爲函數返回值返回地址,一定不能返回指向棧的地址
#include<stdio.h>
#include <stdlib.h>
//void swap(int* pa, int* pb) //無法交換pa pb的值
//{
// int *p = pa;
// pa = pb;
// pb = pa;
//
//}
void swap(int* pa, int* pb) //可以交換pa pb的值
{
int *p = pa;
*pa = *pb;
*pb = *p;
}
int main()
{
int a, b;
int *pa = &a, *pb = &b;
scanf_s("%d %d", pa, pb);
printf("%d %d\n", a, b);
swap(pa,pb);
printf("%d %d\n", a, b);
system("pause");
}
char *p = "你好,明天"; //指針存儲常量字符串首地址,常量字符串不能修改
一級指針75% 二級指針20%,三級 四級指針.....N級指針 用於權限管理???
沒有初始化的指針叫也指針
更高級指針可以改變低級指針的指向
指針聲明一定要初始化
#作用:加上雙引號