练习题目(指针)
Suzhou 55 1
指针初级

计算下列程序输出结果: 练习题目(指针) 上图中pulPtr指向数组第一个元素的地址,解引用为6。 pulPtr+3后指向9的地址,解引用后为9,+=3得到12。


练习题目(指针) 二级指针用来存放一级指针的地址,大小是4字节(32位系统)或8字节(64位系统)。


练习题目(指针) 整型指针+1向后偏移一个整型大小(4字节)。 指针-指针得到的是元素个数。 指针是内存地址,是一串32位整数,可以比较大小。


练习题目(指针) 指针数组:int* arr[5],指针数组创建时候需要指定大小。 数组指针:int (*arr) [5]


写一个函数逆序字符串:

#include <string.h>
#include <assert.h>
void reverse(char* str)
{
    assert(str);
    char* left = str;
    char* right = str + strlen(str) - 1;
    char tmp = 0;
    while (left < right)
    {
        tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}

int main()
{
    char arr[1000] = {0};
    scanf("%s", &arr);
    reverse(arr);
    printf("%s ", arr);
    return 0;
}
#include <string.h>
void reverse(char arr[])
{
    char tmp = 0;
    int i = 0;
    int j = strlen(arr) - 1;
    while (i < j)
    {
        tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
        i++;
        j--;
    }
}

int main()
{
    char arr[1000] = {0};
    scanf("%s", &arr);
    reverse(arr);
    printf("%s ", arr);
    return 0;
}

::: tip scanf读取内容时遇到空格停止。 gets()读取一行内容,包括空格。 :::


练习题目(指针) 代码思路:当第一项的前一项为0时,每一项的值都是前一项×10+第一项。

int main()
{
    int Sn = 0;
    int a = 0;
    int n = 0;
    scanf("%d %d", &a, &n);
    int ret = 0;
    int i = 0;
    for (i = 0;i < n;i++)
    {
        ret = ret * 10 + a;
        Sn += ret;
    }
    printf("%d", Sn);
}

练习题目(指针) 水仙花数是指一个3位数,它的每个位上的数字的3次幂之和等于它本身。例如:1^3 + 5^3 + 3^3 = 153。

#include <math.h>
int main()
{
    int i = 0;
    for (i = 0; i < 100000; i++)
    {
        //判断i是否为水仙花数
        //1.计算i的位数 - n位数
        int n = 1;
        int tmp = i;
        int sum = 0;
        while (tmp /= 10)
        //假设i=125,tmp=125,计算位数时,每次除10去掉一位,位数n+1,
        //当结果不为0时(tmp大于1位),继续除10,直到除10结果为0,跳出循环
        {
            n++;
        }
        //2.计算i的每一位的n次方之和sum
        tmp = i;
        while (tmp)  //所有位数计算完毕后tmp为0
        {
            sum += pow(tmp % 10, n);  //pow函数可以求次方
            tmp /= 10;  //求出最后一位数字的次方后去掉该位
        }
        //3.比较i==sum
        if (i == sum)  //是水仙花数
        {
            printf("%d ", i);
        }
    }
    return 0;
}
0 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 54748 92727 93084

上述程序执行后会提示警告,原因是pow返回的类型是double,而sum类型为int。


练习题目(指针) 这条件谁想得到...:

int main()
{
    int line = 0;
    scanf("%d", &line);
    //打印上半部分
    int i = 0;
    for (i = 0; i < line; i++)
    {
        //打印空格
        int j = 0;
        for (j = 0; j < line - 1 - i; j++)
        {
            printf(" ");
        }
        //打印*
        for (j = 0; j < 2 * i + 1; j++)
        {
            printf("*");
        }
        printf("\n");
    }
    //打印下半部分
    for (i = 0; i < line - 1; i++)
    {
        int j = 0;
        //打印空格
        for (j = 0; j <= i; j++)
        {
            printf(" ");
        }
        //打印*
        for (j = 0; j < 2 * (line - 1 - i) - 1; j++)
        {
            printf("*");
        }
        printf("\n");
    }
    return 0;
}
7
      *
     ***
    *****
   *******
  *********
 ***********
*************
 ***********
  *********
   *******
    *****
     ***
      *

指针进阶

练习题目(指针) A


练习题目(指针) ::: tip 旋转一个字符的步骤: 1.把第一个字符的拿出临时存放在一个空间中 2.其余字符依次向前移动 3.把第一个字符放到字符串最后一个位置

需要旋转k个字符,就把上述步骤执行k次。 :::

  1. 暴力求解:
    #include <string.h>
    void left_move(char* arr, int k)  //指针形式
    {    
     assert(arr);  //assert后的括号内是一个表达式,也可以写成assert(arr != NULL)
     int len = strlen(arr);
     int i = 0;
     for (i = 0; i < k; i++)
     {
         char tmp = *arr;
         int j = 0;
         for (j = 0; j < len - 1; j++)
         {
             *(arr + j) = *(arr + j + 1);
         }
         *(arr + len - 1) = tmp;
     }
    }
    int main()
    {
     char arr[] = "abcdef";
     left_move(arr, 2);  //字符串arr左旋两个字符
     printf("%s\n", arr);
     return 0;
    }

::: tip 只要传参时候写成指针形式,在函数内部就必然使用解引用操作;只要涉及解引用操作,就会涉及指针有效性的问题,需要加上assert断言arr是否有效,如果arr是空指针NULL,程序就会报错(为0报错,非0不报错)。 :::

#include <string.h>
#include <assert.h>
void left_move(char arr[], int k)  //数组形式 
{
    int i = 0;
    int tmp = 0;
    int len = strlen(arr);
    for (i = 0; i < k; i++)
    {
        tmp = arr[0];
        int j = 0;
        for (j = 0; j < len - 1; j++)
        {
            arr[j] = arr[j + 1];
        }
        arr[len - 1] = tmp;
    }
}
int main()
{
    char arr[] = "abcdef";
    left_move(arr, 2);  //字符串arr左旋两个字符
    printf("%s\n", arr);
    return 0;
}
  1. 三步翻转: ::: tip 代码思路如下:
    abcdef
    ba fedc  //分段后两段分别逆序
    cdefab   //再整体逆序
    也可以先逆序整体,再分段逆序。 :::
    #include <string.h>
    void left_move(char arr[],int k)
    {
     int left = 0;
     char tmp = 0;
     int i = 0;
     int len = strlen(arr);
     //分段逆序1
     for (left = 0; left < k; left++)
     {
         tmp = arr[left];
         arr[left] = arr[k - 1];
         arr[k - 1] = tmp;
     }
     //分段逆序2
     for (k; k < len - 1 - i; k++)
     {
         tmp = arr[k];
         arr[k] = arr[len - 1 - i ];
         arr[len - 1 - i ] = tmp;
         i++;
     }
     i = 0;
     //整体逆序
     for (left = 0; left < len - 1 - i; left++)
     {
         tmp = arr[left];
         arr[left] = arr[len - 1 - i];
         arr[len - 1 - i] = tmp;
         i++;
     }
    }
    int main()
    {
     char arr[] = "abcdef";
     left_move(arr,2);  //2为要旋转的字符数
     printf("%s\n", arr);
     return 0;
    }
    cdefab
    上述代码可以优化为在left_move中放一个reverse函数逆序:
    #include <string.h>
    #include <assert.h>
    void reverse(char* left, char* right)
    {
     assert(left);
     assert(right);
     char tmp = 0;
     while (left < right)
     {
         tmp = *left;
         *left = *right;
         *right = tmp;
         left++;
         right--;
     }
    }
    void left_move(char* arr, int k)
    {
     assert(arr);
     int len = strlen(arr);
     assert(k <= len);  //确保要旋转的字符数量不超过字符串长度
     reverse(arr, arr + k - 1);  //逆序左边
     reverse(arr + k, arr + len - 1);  //逆序右边
     reverse(arr, arr + len - 1);  //逆序整体
    }
    int main()
    {
     char arr[] = "abcdef";
     left_move(arr, 2);  //2为要旋转的字符数
     printf("%s\n", arr);
     return 0;
    }
    cdefab

练习题目(指针)

#include <string.h>
#include <assert.h>
void reverse(char* left, char* right)
{
    assert(left);
    assert(right);
    char tmp = 0;
    while (left < right)
    {
        tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}
void left_move(char* arr, int k)
{
    assert(arr);
    int len = strlen(arr);
    assert(k <= len);
    reverse(arr, arr + k - 1);  //逆序左边
    reverse(arr + k, arr + len - 1);  //逆序右边
    reverse(arr, arr + len - 1);  //逆序整体
}
int is_left_move(char* s1, char* s2)
{
    int len = strlen(s1);
    int i = 0;
    //让s1每次旋转一个字符,然后和s2进行比较,直到把s1旋转后可能有的组合情况全部比较完
    for (i = 0; i < len; i++)
    {
        left_move(s1, 1);  //旋转过程中s1在不断变化,+1可以使s1每次多旋转一个字符,不能+i
        int ret = strcmp(s1, s2);
        if (ret == 0)
        {
            return 1;  //s1旋转可以得到s2,返回1
        }
    }
    return 0;
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "cdefab";
    int ret = is_left_move(arr1, arr2);
    if (ret == 1)
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
    return 0;
}
Yes

代码优化: 代码思路:如果在原字符串末尾追加一个该字符串本身,组成的新字符串包含原字符串旋转得来的所有可能组合。

字符串库函数

字符串追加的库函数是strcat和strncat。 练习题目(指针) 练习题目(指针) 如上,strncat中的count参数指定了追加的字符个数。 在字符串追加的过程中,以“\0”作为追加结束的标志。 ::: warning strcat不能追加自己,原因是在字符串追加的过程中,字符一个一个追加到原字符串的后面,原字符串的“\0”被自己的第一个字符覆盖,在后续的追加过程中,没有“\0”被追加到字符串后面,追加不能停止,程序崩溃。 ::: 在一个字符串中寻找另一个字符串的函数是strstr。 strstr(str1,str2)表示在str1指向的字符串中寻找str2指向的字符串,如果能找到,返回子串的首字符地址,如果找不到,返回空指针。返回类型是字符指针类型。

    #include <string.h>
    #include <assert.h>
    int is_left_move(char* str1, char* str2)
    {
        assert(str1);
        assert(str2);
        int len1 = strlen(str1);
        int len2 = strlen(str2);
        if (len1 != len2)
        {
        //两个字符串不相等时,一定不是旋转的得来的
            return 0;
        }
        //在str1的后面追加一个str1
        strncat(str1, str1, 6);
        //判断str2指向的字符串是不是str1指向的字符串的字串
        char* ret = strstr(str1, str2);
        if (ret == NULL)
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
    int main()
    {
        char arr1[15] = "abcdef";
        char arr2[] = "cdefab";
        int ret = is_left_move(arr1, arr2);
        if(ret == 1)
        {
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
        return 0;
    }

练习题目(指针) ::: tip 时间复杂度O(N): 如果数组有N个元素,从前向后遍历,找一个元素是否存在,最坏的情况是查找n次,此时时间复杂度为0(N)。 ::: 题目要求时间复杂度小于O(N),即不能使用遍历的方法。 代码思路:在数字矩阵中,由于矩阵右上角数字是同行最大同列最小,当判断出矩阵右上角的数字和要查找的数字的大小关系时,可以排除一行或一列,查找效率高。如下图 练习题目(指针) 同样使用矩阵左下角的数字进行比较效果和右上角数字相同。

int FindNum(int arr[4][4], int k, int row, int col)
{
    int x = 0;
    int y = col - 1;
    while (x < row && y >= 0)
    {
        if (arr[x][y] > k)
        {
            y -= 1;
            col--;
        }
        else if (arr[x][y] < k)
        {
            x += 1;
        }
        else
        {
            printf("位置是arr[%d][%d]\n", x, y);
            return 1;
        }
    }
    return 0;
}
int main()
{
    int arr[4][4] = { {1,2,3,4} ,{5,6,7,8},{9,10,11,12},{13,14,15,16} };
    int k = 0;
    scanf("%d", &k);
    int ret = FindNum(arr,k,4,4);
    if (ret == 0)
    {
        printf("没有找到\n");
    }
    else
    {
        printf("找到了\n", ret);
    }
    return 0;
}
8
位置是arr[1][3]
找到了

上述代码中为了保持代码段的整洁,自定义函数内不设置输出语句。 代码优化:

int FindNum(int arr[4][4], int k, int* px, int* py)
{
    int x = 0;
    int y = *py - 1;
    while (x < *px && y >= 0)
    {
        if (arr[x][y] > k)
        {
            y -= 1;
            (*py)--;
        }
        else if (arr[x][y] < k)
        {
            x += 1;
        }
        else
        {
            *px = x;
            *py = y;
            return 1;
        }
    }
    return 0;
}
int main()
{
    int arr[4][4] = { {1,2,3,4} ,{5,6,7,8},{9,10,11,12},{13,14,15,16} };
    int k = 0;
    int x = 4;
    int y = 4;
    scanf("%d", &k);
    int ret = FindNum(arr, k, &x, &y);
    if (ret == 0)
    {
        printf("没有找到\n");
    }
    else
    {
        printf("找到了\n", ret);
        printf("位置是arr[%d][%d]\n", x, y);
    }
    return 0;
}
8
找到了
位置是arr[1][3]

::: tip 上述代码使用传址调用既把主函数中矩阵坐标传进自定义函数中,又把自定义函数中得到的矩阵坐标返回到主函数中,这种参数设计称为返回型参数。 :::

评论区

索引目录