-4

I am writing a program in C to separate the hour and minutes using arrays. However, the program gives me some outputs which exceed the length of the defined array. Can someone explain to me? I want the two array only include hour and the rest of the time without (AM/PM).

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>

int main() {
    char* s = (char *)malloc(10 * sizeof(char));
    s="11:22:33AM";

    printf("The time is %s\n",s);

    char Hour[2];
    char Minutes[6];
    int i;
    printf("The hour is %s\n",Hour);
    printf("The minute is %s\n",Minutes);

    for (i=0;i<2;i++){
        Hour[i]=s[i];
    }
   for (i=2;i<8;i++){
        Minutes[i-2]=s[i];
    }

    printf("%d\n",sizeof(Hour));
    printf("%d\n",sizeof(Minutes));

    printf("The hour is %s\n",Hour);
    printf("The minute is %s\n",Minutes);

}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Show the right and wrong output that you are getting – hungersoft Mar 17 '18 at 08:01
  • 1
    The `%s` specifier is for **null-terminated strings** but your arrays are fixed-length strings. You should use the format-string specifier that sets the length of the output instead. – Dai Mar 17 '18 at 08:02
  • You do not need to allocate to assign a pointer to a `string-literal`, e.g. `char *s = "11:22:33AM";` is fine. You should still visit: [**Do I cast the result of malloc?**](http://stackoverflow.com/q/605845/995714). But, **note:** the *string-literal* is immutable as it resides in *read-only* memory. You are one-char too short with `Hour[2]`, `char Hour[3] = "";` would be a better declaration. – David C. Rankin Mar 17 '18 at 08:02
  • Why do you malloc space then immediately leak the space? – M.M Mar 17 '18 at 08:07
  • 1
    Why do you printf uninitialized buffers? – M.M Mar 17 '18 at 08:07
  • @m.m it's obvious that he doesn't know that the assignment operator does not copy strings. He should use strcpy(), or something similar. – machine_1 Mar 17 '18 at 08:11
  • You don't allocate enough space with your `malloc()` call to hold the (null-terminated) string. Fortunately, you leak the allocated memory immediately by overwriting the pointer with a pointer to the string. This saves you from some sources of crashing, but leaking memory is not good either in general. You'll be OK here because the program is tiny. You print uninitialized strings. You print `size_t` values (from `sizeof()`) with the incorrect format specifier (it should be `%zu` — assuming you have a C99 or later compiler; if you're stuck with C90, there isn't a portable way to print it). – Jonathan Leffler Mar 17 '18 at 08:33

3 Answers3

1

The %s specifier is for null-terminated strings but your arrays are fixed-length character buffers. You should use the format-string specifier that sets the length of the output instead %.{length}s:

printf( "The hour is %.2s\n", Hour );

See the documentation for printf: http://www.cplusplus.com/reference/cstdio/printf/

.precision : this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.

As your character-buffers do not contain terminal nulls '\0' there won't be a null character so the computer will keep on scanning memory until it does encounter a zero.

An alternative is to increase the size of the buffers and set their last character value to nulls.

Dai
  • 141,631
  • 28
  • 261
  • 374
  • Thanks. That is the answer I am looking for. I thought every string has a terminator by default. A new thing to learn about string. – Jingwei Wang Mar 17 '18 at 08:51
  • @JingweiWang It does, a string by definition is null terminated. If it does not have a null terminator it's not a string. – Fredrik Mar 17 '18 at 09:17
  • @Dai this question is about C, and in C it's only a string if it's null terminated. – Fredrik Mar 17 '18 at 09:21
  • @Dai sure, give me a minute. – Fredrik Mar 17 '18 at 09:23
  • 1
    No: C also has fixed size character arrays without null terminators, but strings are defined as having null terminators: [C11 §7.1.1 Definitions of terms](https://port70.net/~nsz/c/c11/n1570.html#7.1.1): **A _string_ is a contiguous sequence of characters terminated by and including the first null character.** – Jonathan Leffler Mar 17 '18 at 09:25
  • 1
    "A string is a contiguous sequence of characters terminated by and including the first null character.." ISO C STANDARD – Fredrik Mar 17 '18 at 09:27
  • 1
    So to summarize, if it does not have a null character, it's just a sequence of characters and not a string. – Fredrik Mar 17 '18 at 09:27
  • 1
    @Fredrik I stand corrected. Thank you for the clarification. I didn't realise the C specification defined it like that. I was operating on the assumption that the terms "character sequence" and "string" were strictly synonymous. – Dai Mar 17 '18 at 09:30
  • 1
    @Fredrik I've updated my answer with what I hope you'll agree is a more stringently correct response. – Dai Mar 17 '18 at 09:33
  • @Dai looks better, it may not be a very important distinction but answers on SO should be as correct as possible :) – Fredrik Mar 17 '18 at 09:35
1

You just need to add at the end of both arrays the terminate character to create a null-terminated string. So your code will become:

char Hour[3];
char Minutes[7];
.
.
.
Hour[2] = '\0';    //or  Hour[2] = 0;
Minutes[6] = '\0'; //or  Minutes[6] = 0;

Also don't forget at the end of the program to free the dynamically allocated memory free(s);

chanioxaris
  • 946
  • 10
  • 9
1

There are a number of approaches you can take. There is no need to allocate, as your initial time can be assigned directory to s as a string-literal. Then it is simply a matter of filling hours and minutes with strncpy to separate the time. No loops required. Also, sizeof hours or sizeof minutes return the total size of the arrays, not the length within. For that (and since you initialized the arrays to all zero providing default nul-termination when one-less than the total number of characters are filled -- you can use both as strings, e.g.

#include <stdio.h>
#include <string.h>

int main (void) {

    char *s = "11:22:33AM",     /* no allocation required */
        hour[3] = "",           /* initialize all arrays to zero */
        minutes[6] = "";

    printf ("The time is %s\n", s);

    /* use strncpy to fill both hour and minutes */
    printf ("The hour is %s\n", strncpy (hour, s, 2));
    printf ("The minute is %s\n", strncpy (minutes, s + 3, 5));

    printf ("%zu\n", strlen (hour));
    printf ("%zu\n", strlen (minutes));

    printf("The hour is %s\n", hour);
    printf("The minute is %s\n", minutes);

}

Example Use/Output

$ ./bin/splittime
The time is 11:22:33AM
The hour is 11
The minute is 22:33
2
5
The hour is 11
The minute is 22:33

Look things over and let me know if that is what you intended. If not, drop a comment and I'm happy to help further.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thanks. It is nice to learn a new function. strncpy. I just did not realize that an array is not ended with '\0' by default. – Jingwei Wang Mar 17 '18 at 08:56
  • Sure, glad to help. That is the key to strings in C. If it's not *nul-terminated* -- "it ain't a string... it's just an array". Consequently you cannot use any of the functions that expect a "string" -- unless the array (or block of memory) you provide is terminated by the *nul-character* `'\0'` (or just zero, as ASCII `nul` has the decimal value '0') AND all characters leading up to the *nul-terminating* char must be validly initialized. (meaning you can't do `char s[5]; s[4]=0; strncpy (s, "Hi", 2);` and have a valid string `:)` – David C. Rankin Mar 18 '18 at 03:22