🎯 int *p(char*) vs int (*p)(char*) β€” Complete Lesson
0%
C Pointers  Β·  Function Pointers  Β·  Declarations

Two Declarations β€”
One Letter of Difference

Adding parentheses around *p completely changes what p is. Without them p is a function that returns a pointer. With them p is a pointer that holds a function. Four examples β€” each builds on the last.

int *p(char *a)
p is a function. Takes a char pointer. Returns an int pointer β€” an address. You call p and get back a memory location.
int (*p)(char *a)
p is a pointer. Points at a function. That function takes char* and returns int. You assign a function to p and call through it.
S1
Reading the declarations
S2
int *p(char *a) β€” word length stored in heap
S3
int (*p)(char *a) β€” function pointer, count vowels
S4
Both together β€” one program
Step 1 πŸ“– Reading the Two Declarations β€” What Each Part Means
S1
How to Read Each Declaration β€” Right Before Left
Start at p, look right first, then left β€” parentheses change the binding order
Reading Rule
The One Rule β€” Right Binds Before Left
Start at the name p. Look right β€” if you see () or [] they bind before the * on the left. So int *p(char *a) β€” p sees (char *a) on its right first β†’ p is a function. The * then applies to the return type β†’ it returns int*.

Putting (*p) in parentheses breaks that rule β€” the parens force p to bond with * first β†’ p is a pointer. Then (char *a) on the outside describes the function it points to.
Annotated reading β€” both declarations broken down token by token
int *p(char *a) β€” p is a FUNCTION returning int* int * p (char *a) β‘’ return type β‘’ it's a pointer β‘  start here β‘‘ right first β†’ function Read: "p is a function taking char* β€” returns int*" int (*p)(char *a) β€” p is a POINTER to a function int ( * p ) (char *a) β‘  start β‘‘ * β†’ pointer β‘’ fn taking char* β‘£ fn returns int Read: "p is a pointer to a function taking char* returning int"
int *p(char *a) β€” step by step
β‘  Start at p
β‘‘ Right: (char *a) β†’ p is a function taking char*
β‘’ Left: * β†’ return type is a pointer
β‘£ Far left: int β†’ pointer to int

Result: p is a function(char*) β†’ int*
int (*p)(char *a) β€” step by step
β‘  Start at p
β‘‘ Parens: (*p) β†’ p is a pointer
β‘’ Right: (char *a) β†’ to a function taking char*
β‘£ Far left: int β†’ function returns int

Result: p is a pointer to function(char*) β†’ int
The single visual test: Is there a (*p) group? Yes β†’ p is a pointer to a function. No β†’ p is a function that returns a pointer. That one bracket pair is the only difference.
Step 2 πŸ”΅ int *p(char *a) β€” Function Returning int* β€” Word Length on Heap
S2
int *getLength(char *a) β€” Computes length, stores in heap, returns address
Caller receives int* β€” must dereference to read value β€” must free() when done
Function β†’ int*
int *getLength(char *a) matches the declaration int *p(char *a) exactly. It is a function β€” not a pointer. It counts the characters in the string, stores that count in heap memory using malloc, and returns the address of that memory. The caller receives an int* β€” a pointer β€” not the value directly. To read the count, the caller must dereference with *result. To avoid a memory leak, the caller must call free(result) when finished.
What the function returns β€” address on heap, not the value itself
int *getLength( char *a) counts chars in a[] returns int* int *result = 0x9f20 (addr) points to heap memory 8 length of "Password" *result = 8 free(result) after a = "Password"
s2_fn_returns_ptr.c
C
#include <stdio.h>
#include <stdlib.h>

/*  int *p(char *a)
    ─────────────────────────────────────────────────
    p is a FUNCTION
      takes   : char *a  (pointer to a string)
      returns : int*     (address of an integer)
    ─────────────────────────────────────────────────  */
int *getLength(char *a) {

    int *r = (int*)malloc(sizeof(int)); /* heap space for one int */

    int i = 0;
    while (a[i] != '\0') i++;    /* count characters       */

    *r = i;                       /* write count into heap  */
    return r;                     /* return the ADDRESS      */
}

int main() {
    char *word = "Password";     /* 8 characters           */

    int *result = getLength(word); /* result holds an ADDRESS */

    printf("Word     : %s\n",    word);
    printf("Address  : %p\n",    (void*)result);
    printf("Length   : %d\n",   *result); /* dereference to read */

    free(result);   /* release heap β€” always pair malloc with free */
    return 0;
}
output
Word     : Password
Address  : 0x55f3a2c1d010
Length   : 8
Why store in heap? A local variable inside getLength would vanish when the function returns. Heap memory (malloc) stays alive until you call free(). This is the reason functions that return a pointer must either use heap memory, a static variable, or a pointer to something the caller owns.
Step 3 🟣 int (*p)(char *a) β€” Pointer to a Function β€” Count Vowels
S3
int (*p)(char *a) β€” p stores a function's address β€” call any matching function through it
Assign countVowels or countConsonants to the same pointer β€” call through p
Pointer to Function
int (*p)(char *a) declares p as a pointer β€” not a function. It stores the address of a function. Any function whose signature matches int someFunction(char *a) can be assigned to p. You call through p just like calling the function directly: p(text). The return value is a plain int β€” no pointer, no malloc, no free. The power: you can swap which function p points to at runtime.
p stores an address from code memory β€” two functions, one pointer
int (*p)(char *a) holds a fn address countVowels() int fn(char *a) β†’ int countConsonants() int fn(char *a) β†’ int p = countVowels p = countConsonants p("Ananta") β†’ 3 p("Ananta") β†’ 3
s3_fn_pointer.c
C
#include <stdio.h>
#include <string.h>

/* Helper: check if char is a vowel */
int isVowel(char c) {
    return strchr("aeiouAEIOU", c) != NULL;
}

/*  First function β€” matches int (*p)(char *a)  */
int countVowels(char *a) {
    int count = 0;
    for (int i = 0; a[i] != '\0'; i++)
        if (isVowel(a[i])) count++;
    return count;         /* plain int β€” no pointer    */
}

/*  Second function β€” same signature, different task */
int countConsonants(char *a) {
    int count = 0;
    for (int i = 0; a[i] != '\0'; i++)
        if (a[i] != ' ' && !isVowel(a[i])) count++;
    return count;
}

int main() {
    char *word = "Ananta";

    /*  int (*p)(char *a)
        ─────────────────────────────────────────
        p is a POINTER to a function
          that takes : char *a
          that returns : int  (a plain value)
        ─────────────────────────────────────────  */
    int (*p)(char *a);          /* declare the pointer         */

    p = countVowels;            /* point p at countVowels      */
    printf("Vowels in '%s'     : %d\n", word, p(word));

    p = countConsonants;        /* re-point p β€” no malloc/free */
    printf("Consonants in '%s' : %d\n", word, p(word));

    return 0;
}
output
Vowels in 'Ananta'     : 3
Consonants in 'Ananta' : 3
Function name without () = its address. p = countVowels stores the address of countVowels in p. p = countVowels() with parentheses would call it and try to store the returned int into p β€” a type error. The parentheses make all the difference.
Step 4 🏁 Both Together β€” One Complete Program
S4
Complete Program β€” int *p(char *a) and int (*p)(char *a) side by side
Same string "Elephant" β€” function returns heap pointer β€” function pointer swaps tasks
Full Example
One string. Two completely different ways to use functions with char* parameters. getFirstCharCode matches int *p(char *a) β€” allocates heap memory and returns its address. countUpper and countLower both match int (*p)(char *a) β€” plain functions that return int values, assigned to the same pointer and called through it.
int *getFirstCharCode(char *a)
Takes string, stores ASCII of first char in heap int, returns its address.

Caller: int *r = getFirstCharCode(w);
Read: printf("%d", *r);
Release: free(r);
int (*p)(char *a)
Pointer that holds either countUpper or countLower.

Assign: p = countUpper;
Call: int n = p(word);
Swap: p = countLower; n = p(word);
s4_both_together.c
C β€” Complete Program
#include <stdio.h>
#include <stdlib.h>

/* ═══════════════════════════════════════════════════
   Part A β€” matches:  int *p(char *a)
   p is a FUNCTION taking char*, returning int*
   stores ASCII code of first character on the heap
═══════════════════════════════════════════════════ */
int *getFirstCharCode(char *a) {
    int *r = (int*)malloc(sizeof(int));
    *r = (int)a[0];  /* a[0] is first char β†’ cast to int = ASCII */
    return r;        /* return ADDRESS of heap int                */
}

/* ═══════════════════════════════════════════════════
   Part B β€” both match:  int (*p)(char *a)
   p is a POINTER to a function(char*)β†’int
   Two functions with the same signature
═══════════════════════════════════════════════════ */
int countUpper(char *a) {
    int n = 0;
    for (int i = 0; a[i] != '\0'; i++)
        if (a[i] >= 'A' && a[i] <= 'Z') n++;
    return n;
}

int countLower(char *a) {
    int n = 0;
    for (int i = 0; a[i] != '\0'; i++)
        if (a[i] >= 'a' && a[i] <= 'z') n++;
    return n;
}

int main() {
    char *word = "Elephant";

    printf("Word: %s\n\n", word);

    /* ── int *p(char *a) ────────────────────────────
       Call function, receive int* (address)          */
    int *code = getFirstCharCode(word);
    printf("int *getFirstCharCode:\n");
    printf("  First char  : '%c'\n", word[0]);
    printf("  Returned    : address %p\n", (void*)code);
    printf("  Value *code : %d  (ASCII of '%c')\n",
           *code, (char)*code);
    free(code);                  /* release heap memory    */

    printf("\n");

    /* ── int (*p)(char *a) ──────────────────────────
       Pointer holds function address, call through it */
    int (*p)(char *a);           /* declare function pointer */

    p = countUpper;              /* point at countUpper      */
    printf("int (*p)(char *a) = countUpper:\n");
    printf("  Uppercase letters : %d\n", p(word));

    p = countLower;              /* re-point at countLower   */
    printf("int (*p)(char *a) = countLower:\n");
    printf("  Lowercase letters : %d\n", p(word));

    return 0;
}
output
Word: Elephant

int *getFirstCharCode:
  First char  : 'E'
  Returned    : address 0x55a3b1c2d010
  Value *code : 69  (ASCII of 'E')

int (*p)(char *a) = countUpper:
  Uppercase letters : 1

int (*p)(char *a) = countLower:
  Lowercase letters : 7
"Elephant" breakdown: First char 'E' = ASCII 69. Uppercase: only E = 1. Lowercase: l e p h a n t = 7. The function pointer calls two completely different functions through the same p variable β€” no malloc, no free, just plain int values returned directly.
summary β€” every difference at a glance
πŸ“‹
Everything Side by Side β€” The Complete Difference
Declaration Β· what p is Β· how to call Β· what you get back Β· memory
Reference
int *p(char *a)
p is: a function
Takes: char* (string pointer)
Returns: int* (memory address)
How to call: int *r = p(text);
To read value: *r
Memory: must free(r) if malloc used
Right side wins: (char*) before *
Example: int *getLength(char *a)
int (*p)(char *a)
p is: a pointer to a function
Takes: char* (passed through to fn)
Returns: int (plain value)
How to use: p = myFn; then p(text);
To read value: assign directly int v = p(text);
Memory: no free needed
Parens win: (*p) binds before (char*)
Example: int (*p)(char *a) = countVowels;
checklist
  • Reading rule: Start at p, go right first. (char *a) on the right β†’ p is a function. (*p) parens lock pointer-ness first β†’ p is a pointer to function.
  • int *p(char *a): p IS a function. Returns int* β€” an address. Caller dereferences with *result to get the value. Must free() if malloc was used inside.
  • int (*p)(char *a): p IS a pointer. Assign any matching function: p = myFn;. Call through it: int v = p(text);. Returns a plain int β€” no pointer, no free.
  • Assign vs call: p = countVowels (no parens) = store the address of countVowels. p = countVowels() (parens) = call the function and try to store its return value β€” a type mismatch.
  • heap vs stack: int *p(char *a) functions must use malloc so the returned address stays valid after the function returns. A local variable inside the function would die β€” its address would be dangling.
  • a[0] vs a: a is char* (an address). a[0] or *a is the actual first character. Cast the character: (int)a[0] β†’ ASCII value. Cast the pointer: (int)a β†’ memory address number (not useful).