6

I'm trying to use strndup function, but I get the error

error: implicit declaration of function 'strndup' [-Werror=implicit-function-declaration]

I have searched around and found that it's not a standard function and therefore I have to compile with different flags. However, I receive same issue by compiling with the following:

-std=gnu11
-Wall
-Wextra
-Werror
-Wmissing-declarations
-Wmissing-prototypes
-Werror-implicit-function-declaration
-Wreturn-type
-Wparentheses
-Wunused
-Wold-style-definition
-Wundef
-Wshadow
-Wstrict-prototypes
-Wswitch-default
-Wunreachable-code
-D_GNU_SOURCE

I'm doing an assignment and therefore I have to use all these, but what I found is that I have to compile with '-D_GNU_SOURCE' for the error to go away, but it doesn't.

EDIT:

I have these included as well:

#define __STDC_WANT_LIB_EXT2__ 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "list.h"

Would very much appreciate help to fix this.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Cows42
  • 307
  • 3
  • 12

4 Answers4

10

The functions strdup and strndup are not yet part of Standard C. They are standardized in Posix.1-2008 and declared in <string.h> on Posix systems. Did you include <string.h>?

If your system does not provide these functions, you can define them yourself this way:

#include <string.h>
#include <stdlib.h>

char *strdup(const char *s) {
    size_t size = strlen(s) + 1;
    char *p = malloc(size);
    if (p != NULL) {
        memcpy(p, s, size);
    }
    return p;
}

char *strndup(const char *s, size_t n) {
    char *p;
    size_t n1;

    for (n1 = 0; n1 < n && s[n1] != '\0'; n1++)
        continue;
    p = malloc(n + 1);
    if (p != NULL) {
        memcpy(p, s, n1);
        p[n1] = '\0';
    }
    return p;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • What is the main difference between your strndup function in comparison to the one posted above by PeterJ_01? – Cows42 Sep 02 '17 at 13:12
  • @Cows42: the version posted by PeterJ_01 does not implement the precise semantics of `strndup()` as specified in Posix (see https://linux.die.net/man/3/strndup ) – chqrlie Sep 02 '17 at 13:20
  • Alright, is it fine that I use your implementation in my program? – Cows42 Sep 02 '17 at 13:21
  • @Cows42: Of course! all code I post on SO is placed into the public domain. – chqrlie Sep 02 '17 at 13:24
  • @chqrlie sorry - trying to escape from the real boring work – 0___________ Sep 02 '17 at 13:36
  • @im_infamous: I rejected your edit because casting the return value of `malloc()` is unnecessary and counterproductive in C. The cast is necessary in C++ but the question is tagged C. – chqrlie May 30 '19 at 12:25
4

Requesting POSIX functions

You may find that you can 'activate' the declarations of strndup() et al by setting appropriate macros to request POSIX support:

#define _XOPEN_SOURCE 700

That strictly is for the X/Open SUS (Single Unix Specification), but that's usually what you want. Strictly enabling only POSIX functionality would require:

#define _POSIX_C_SOURCE 200809L

The -std=gnu11 compiler option usually enables all these (and other special options) by default, so the fact that you're running into problems suggests you may not have the functions available regardless.

If you don't have strndup() available even with the enabling macros set before you include any headers, you can write your own version of the function. The tricky part is making the declaration available in a suitable header. However, as a band-aid, you could make it static in the only source file that needs it. It is also easiest to implement if you have strnlen() available. If you don't have strnlen() either (a plausible problem), then you need to write that too, but the plain C version probably won't match the speed of an assembler version of strnlen() — or use memchr().

strndup()

char *strndup(const char *str, size_t len)
{
    size_t act = strnlen(str, len);
    char *dst = malloc(act + 1);
    if (dst != 0)
    {
        memmove(dst, str, act);
        dst[act] = '\0';
    }
    return dst;
}

(I note that chqrlie shows a neat alternative using memchr() instead of strnlen().)

strnlen()

size_t strnlen(const char *str, size_t len)
{
    for (size_t size = 0; size < len; size++)
    {
        if (str[size] == '\0')
            return size;
    }
    return len;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Just as a note: Using Codeblocks (17.12), the -std=gnu11 option has to be put into the compiler configuration command-line like. (Other compiler options) There is a conflict with the check-box of selecting C11 as standard. – GermanNerd Dec 04 '18 at 10:52
2

Without knowing your code, I guess that you forgot to include string.h.

#include <string.h> 

If you don't include the particular header, the declaration of the function strndup() isn't visible to the compiler. Hence the error message of strndup() not being declared.

Put the above line at the top of your code and it should work.

Eduard Itrich
  • 836
  • 1
  • 8
  • 20
  • Sorry, I forgot to include this in main post. But I already have this in my code: #define __STDC_WANT_LIB_EXT2__ 1 #include #include #include #include "list.h" – Cows42 Sep 02 '17 at 11:56
  • What is your build environment. Are the header files present? – Eduard Itrich Sep 02 '17 at 12:01
  • I use Codelite and I have gcc compiler installed if that's what you're asking. The header files are all present as the other functions are working fine. – Cows42 Sep 02 '17 at 12:04
2

strdup & strndup are not a part of the C standard. They are POSIX functions. So you may not have them in your libraries.

char *mystrndup(const char *src, size_t size)
{
    char *duplicate = NULL;
    size_t len;

    if (src != NULL ) //for @chqrlie
    {
        len = strlen(src);
        size = len > (size) ? size + 1 : (size = len + 1))
        duplicate = calloc(sizeof(char), size);

        if (duplicate != NULL)
        {
            strncpy(duplicate, src, size - 1);
        }
        else
        {
            errno = ENOMEM;
        }
    } 
    else
    {
        errno = EINVAL;
    }
    return duplicate;
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • How do I make it work then? I updated the main post with what I include in my code as well. – Cows42 Sep 02 '17 at 11:58
  • If you do not have them you need to write one. – 0___________ Sep 02 '17 at 12:00
  • `strdup()` et al. are indeed POSIX, and supported in glibc; but it appears that OP is using Windows, where support for POSIX is always questionable.... – ad absurdum Sep 02 '17 at 12:20
  • Why? More details? – 0___________ Sep 02 '17 at 12:38
  • @chqrlie - is 100% corrent as that is the caller problem to provide a valid strin for duplication. – 0___________ Sep 02 '17 at 12:53
  • @chqrlie it cannot be `size + 1` – 0___________ Sep 02 '17 at 12:54
  • @PeterJ_01: OK, it is a quality of implementation issue. `strlen(src)` may be unnecessary costly of the source string is very long. – chqrlie Sep 02 '17 at 12:57
  • it is an example for the OP – 0___________ Sep 02 '17 at 12:58
  • @chqrlie no - i copy size -1. last is always zero (calloc) – 0___________ Sep 02 '17 at 12:58
  • copying `size-1` is incorrect: `strndup()` copies *at most size bytes*. Furthermore you would have undefined behavior if `size == 0`. – chqrlie Sep 02 '17 at 12:59
  • @chqrlie no - your logic is wrong. If the function is called with the parameter size == 0 it is just wrong call, same as src not beeing a C string or NULL. It is up to caller. He says what is the max buffer size.. – 0___________ Sep 02 '17 at 13:02
  • @chqrlie especially for you added the check. :D – 0___________ Sep 02 '17 at 13:07
  • 1
    Posix: *The `strndup()` function shall be equivalent to the `strdup()` function, duplicating the provided s in a new block of memory allocated as if by using `malloc()`, with the exception being that `strndup()` copies at most `size` plus one bytes into the newly allocated memory, terminating the new string with a NUL character. If the length of `s` is larger than `size`, only `size` bytes shall be duplicated. If `size` is larger than the length of `s`, all bytes in s shall be copied into the new memory buffer, including the terminating NUL character.* – chqrlie Sep 02 '17 at 13:08
  • *The newly created string shall always be properly terminated.* – chqrlie Sep 02 '17 at 13:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153522/discussion-between-chqrlie-and-peterj-01). – chqrlie Sep 02 '17 at 13:09
  • 1
    Note that standard library functions do not set `errno` to zero. – Jonathan Leffler Sep 02 '17 at 13:43