关于指针是个什么,大家可以参考C初级专栏5.指针与指针变量博文
一数组指针
二指针数组
数组指针
指向数组的指针,指针变量存放的是数组的地址1
#include <stdio.h>
int main() {
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int(*ptr_int)[10] = &arr;
printf("%d\n", ((int*)(ptr_int + 1))[-1]);
// 数组指针向后偏移一个单位就相当于偏移整个数组长度个单位量
return 0;
}
需要注意的是:数组指针向后偏移一个单位就相当于地址偏移整个数组长度个单位量
还有需要注意数组传参需要使用到数组指针:
一维数组传参
#include <stdio.h>
void test(int arr[10], int size)//ok?
{
for (int i = 0; i < size; ++i)
printf("%d ", arr[i]);
printf("\n");
}
void test1(int arr[], int size)//ok?
{
for (int i = 0; i < size; ++i)
printf("%d ", arr[i]);
printf("\n");
}
void test2(int* arr, int size)//ok?
{
for (int i = 0; i < size; ++i)
printf("%d ", arr[i]);
printf("\n");
}
void test3(int* arr_ptr[10], int size)//ok?
{
for (int i = 0; i < size; ++i)
printf("%d ", *arr_ptr[i]);
printf("\n");
}
void test4(int** arr_ptr, int size)//ok?
{
for (int i = 0; i < size; ++i)
printf("%d ", *arr_ptr[i]);
printf("\n");
}
int main()
{
int arr[10] = { 0 };
for (int i = 0; i < 10; ++i)
arr[i] = i + 1;
int* arr_ptr[10] = { 0 };
for (int i = 0; i < 10; ++i)
arr_ptr[i] = arr + i;
test(arr, 10);
test1(arr, 10);
test2(arr, 10);
test3(arr_ptr, 10);
test4(arr_ptr, 10);
return 0;
}
test~test2都是一维整形数组传参方式,int arr[元素个数]、int arr[]、 int* arr;
test3~4是整形指针数组的传参方式,int* arr_ptr[元素个数]、int* *arr_ptr;
二维数组传参
#include <stdio.h>
void test1(int arr[3][5], int row, int column)//ok?
{
for (int i = 0; i < row; ++i){
for (int j = 0; j < column; ++j)
printf("%d ", arr[i][j]);
printf("\n");
}
printf("\n");
}
void test2(int arr[][5], int row, int column)//ok?
{
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j)
printf("%d ", arr[i][j]);
printf("\n");
}
printf("\n");
}
//总结:二维数组传参,函数形参的设计只能省略第一个口的数字。
void test3(int* arr, int row, int column)//ok?
{
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j)
printf("%d ", (arr + i * 5)[j]);
printf("\n");
}
printf("\n");
}
void test4(int* arr[5], int row, int column)//ok?
{
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j)
printf("%d ", (arr+i*5)[j]);
printf("\n");
}
printf("\n");
}
void test5(int(*arr)[5], int row, int column)//ok?
{
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j)
printf("%d ", arr[i][j]);
printf("\n");
}
printf("\n");
}
void test6(int** arr, int row, int column)//ok?
{
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j)
//printf("%d ", (arr + i * 5)[j]);
printf("%d ", arr[i * 5][j]);
printf("\n");
}
printf("\n");
}
int main() {
int arr[3][5] = { 0 };
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 5; ++j)
arr[i][j] = i*5 + j;
test1(arr, 3, 5);
test2(arr, 3, 5);
test3(arr, 3, 5);
test4(arr, 3, 5);
test5(arr, 3, 5);
test6(arr, 3, 5);
return 0;
}
test1~test6都可以完成二维数组传参得到过程
指针数组
存放指针的数组
#include <stdio.h>
int main() {
int a=1, b=2, c=3, d=4;
int* arr_ptr[4] = { &a, &b, &c, &d };
for (int i = 0; i < 4; ++i)
printf("%d\n", *arr_ptr[i]);
return 0;
}
指针数组arr_ptr存放的是int*类型的指针
函数指针和回调函数
函数指针:指向函数的指针
定义一个整形元素比较函数
int compare(const void arg1, const void arg2){
return (int)arg1-(int)arg2;
}
定义一个函数指针指向compare
int *ptr_func(const void *arg1, const void *arg2) = compare;
回调函数
回调函数就是通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一 个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或 条件进行响应。
举个栗子:
<stdlib.h> and <search.h>中定义的快排函数qsort最后一个参数需要一个比较函数的函数地址
qsort( (void *)argv, (size_t)argc, sizeof( char * ), compare );
qsort快排函数第一个参数是要排序的空间的首地址,第二个参数argc是元素的个数,第三个参数是一个元素的字节大小,第三个参数是用户提供的元素大小比较函数
#include <stdio.h>
int compare(const void* arg1, const void* arg2) {
return *(int*)arg1 - *(int*)arg2;
}
int main() {
int (*ptr_func)(const void* , const void* ) = compare;
int arr[] = { 4, 6, 3, 1, 9, 8, 5, 2 };
int args = sizeof(arr) / sizeof(int);
qsort((void*)arr, args, sizeof(int), ptr_func);
for (int i = 0; i < args; ++i)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
函数指针数组
通俗说就是存放函数指针的数组
BC6小飞机
函数指针数组方式:
#include <stdio.h>
void func1(){
printf(" ** \n");
}
void func2(){
printf("************\n");
}
void func3(){
printf(" * * \n");
}
int main()
{
void (*p[3])() = {
func1, func2, func3};
for(int i=0; i<3; ++i){
p[i]();
p[i]();
}
return 0;
}
函数指针数组定义格式:函数返回值+(*指针数组名)+(参数列表)+初始化列表
常见面试题
笔试题1:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf("%d, %d" *(a + 1), *(ptr - 1));
return 0;
}
//程序的结果是什么?
2, 5
笔试题2:
//通过内存对齐的知识,我们可以计算出来struct Test大小为20
#include <stdio.h>
struct Test
{
int Num;
char pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p的值为0x100000。如下表表达式的值分别为多少? int main()
int main() {
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
p + 0x1,指针地址偏移一个单位,就偏移了结构体大小个字节0x100014
(unsigned long)p + 0x1 先强转为无符号long四个字节,不管在32或者64位平台下,有没有发生截断,最终值都是0x100001
(unsigned int*)p + 0x1 先将结构体指针强转为无符号整形指针,加一,地址偏移四个字节,最终:0x100004
笔试题3:
int main()
{
int a[4] = { 1, 2, 3, 4 );
int *ptrl = (int *)(&a + 1);
int *ptr2 = ((int)a + 1);
printf("%x,%x”, ptrl[-l], *ptr2);
return 0;
}
结果是:
4, 000000032
笔试题4:
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf("%d", p[0]);
return 0;
}
1
笔试题5:
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2]-&a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
&p[4][2]-&a[4][2] = -4 转为16进制补码 10000…100 11111…011 11111…100 = FFFFFFFC
FFFFFFFC, -4
笔试题6:
#include <stdio.h>
int main() {
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptrl = (int*)(&aa + 1);
int* ptr2 = (int*)(*(aa + 1));
printf("%d,%d", *(ptrl - 1), *(ptr2 - 1));
return 0;
}
10,5
笔试题7:
#include <stdio.h>
int main()
{
char* a[] = { "work", "at", "alibaba"};
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
at
笔试题8:
#include <stdio.h>
int main() {
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char **cp[] = {c + 3, c + 2, c + 1, c};
char*** cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *-- * ++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
POINT ER ST EW
**++cpp,前置++优先级高于*。先++cpp,cpp为地址就变成了c+2元素的地址两个**就得到"POINT"的首元素地址
*-- * ++cpp,前置++优先级高于*,前置–优先级高于*。++cpp,cpp保存的是c+1的地址,解引用后再–得到元素c的地址
*cpp[-2],[]优先级高于*。cpp[-2],得到c+3元素,解引用得到"FIRST"的首地址,+3就是ST
cpp[-1][-1] + 1。cpp[-1]得到c+2元素地址,再[-1]得到"NEW"的首地址,然后+1,得到EW