Take the following function:
char * slice(const char * str, unsigned int start, unsigned int end) {
int string_len = strlen(str);
int slice_len = (end - start < string_len) ? end - start : string_len;
char * sliced_str = (char *) malloc (slice_len + 1);
sliced_str[slice_len] = '\0';
// Make sure we have a string of length > 0, and it's within the string range
if (slice_len == 0 || start >= string_len || end <= 0) return "";
for (int i=0, j=start; i < slice_len; i++, j++)
sliced_str[i] = str[j];
return sliced_str;
}
I can call this as follows:
char * new_string = slice("old string", 3, 5)
Is there a way to be able to "omit" an argument somehow in C? For example, passing something like the following:
char * new_string = slice("old string", 3, NULL)
// NULL means ignore the `end` parameter and just go all the way to the end.
How would something like that be done? Or is that not possible to do in C?
Optional arguments (or arguments that have default values) are not really a thing in C. I think you have the right idea by passing in 'NULL', except for that NULL is equal to 0 and will be interpreted as an integer. Instead, I would recommend changing the argument to a signed integer instead of unsigned, and passing in a -1 as your flag to indicate that the argument should be ignored.
There's only two ways to pass optional arguments in C, and only one is common. Either pass a pointer to the optional argument and understand NULL as not passed, or pass an out-of-range value as not passed.
Way 1:
char * slice(const char * str, const unsigned int *start, const unsigned int *end);
// ...
const unsigned int three = 3;
char * new_string = slice("old string", &three, NULL)
Way 2:
#include <limits.h>
char * slice(const char * str, const unsigned int start, const unsigned int end);
char * new_string = slice("old string", 3, UINT_MAX);
BTW, this example should really be using size_t and SIZE_MAX but I copied your prototype.
The proposed dupe target is talking about vardiac functions, which do have optional arguments, but it's not like what you're asking for. It's always possible in such a function call to determine if the argument is (intended to be) present by looking at the arguments that come before. In this case, that won't help at all.
Related
I need it to only print the first capital of each word The point of this is to make acronyms from sentences by calling the function above.
EX.
input: Hello output: H.
input: MACinery Bean output: M.C.B.
Desired output: M.B.
void CreateAcronym(char userPhrase[50], char userAcronym[50]){
int len, up, uptwo;
char upper[50];
int isupper();
len = strlen(userPhrase);
for (int i = 0; i < len; i++){
up = isupper(userPhrase[i]);
uptwo = isupper(userPhrase[i+1]);
if (up != 0 && uptwo == 0){
strcpy(&upper[i], &userPhrase[i]);
printf("%c.", upper[i]);
}
}
printf("\n");
The first letter of each word is either the beginning of the string or the previous character being a space. So you must test for that in your condition, not compare the current character and the next character.
for (int i = 0; i < len; i++){
if ((i == 0 || userPhrase[i-1] == ' ') && isupper(userPhrase[i]) {
printf("%c.", userPhrase[i]);
}
}
int isupper();
This is a strange prototype for the <ctype.h> function isupper. I'm not really sure if it's well defined. There are two ways I know to get a correct prototype for this function:
#include <ctype.h>, typically somewhere near the top of your file. This is the way we most often see.
int isupper(int); will also introduce the correct prototype.
Nonetheless I have no doubt the default argument promotions that occur are causing the correct type to be passed, so even if the behaviour is undefined it's probably no trouble.
We need to talk some more about that int argument, though, hence bringing it to your attention. We're expected to ensure that int argument is either an unsigned char value or EOF, but the value you passed is instead a char value that may be neither. When passing char values to <ctype.h> functions, we should (generally, but not always) cast the argument to unsigned char like so:
up = isupper((unsigned char) userPhrase[i]);
As far as the rest of your code goes, I think perhaps you may need to think more about what your algorithm is meant to do. I'ma use a functional style, because I think it makes sense for the sake of explanation here. You should translate this to a procedural style for class. So for my example (which may not even compile 🤷♂️ but is there to help you think about your algorithm) you can expect my continuations to be facilitated by something like:
typedef int fun(), contin(char *, char const *, fun **);
It seems to me like you want to skip leading non-alphabet characters, extract the first alpha, then skip any alpha until you reach non-alpha again. For example, the first step:
int skip_nonalpha(char *dst, char const *src, fun **f) {
if (!*src) return *dst = 0;
if (!isalpha((unsigned char) *src)) return skip_nonalpha(dst, src + 1, f);
return ((contin *) *f)(dst, src, f + 1);
}
In the function above you can see the argument passed to isalpha is explicitly converted to unsigned char, as I mentioned earlier. Anyhow, the next step is to extract and convert to upper:
int extract_toupper(char *dst, char const *src, fun **f) {
*dst = toupper(*src);
return ((contin *) *f)(dst + 1, src + 1, f + 1);
}
We don't need the explicit conversion in the function above because we already know src is alpha, which makes it a positive value (within the range of [0..UCHAR_MAX]). Next, skip the rest of the alpha characters:
int skip_alpha(char *dst, char const *src, fun **f) {
if (!*src) return *dst = 0;
if (isalpha((unsigned char) *src)) return skip_alpha(dst, src + 1, f);
return ((contin *) *f)(dst, src, f + 1);
}
Finally, wrapping it all up:
int make_acronym_tail(char *dst, char const *src, fun **f) {
return skip_nonalpha(dst, src,
(fun *[]){ (fun *) extract_alpha
, (fun *) skip_alpha
, (fun *) make_acronym_tail)
});
}
int make_acronym(char *dst, char const *src) { return make_acronym_tail(dst, src, NULL); }
Now assuming this compiles you should be able to use it like:
char phrase[] = "hello world", acronym[strlen(phrase) + 1];
make_acronym(acronym, phrase);
This question already has answers here:
Why does strchr take an int for the char to be found?
(4 answers)
Closed 6 years ago.
Trying to create a simple function that would look for a single char in a string "like strchr() would", i did the following:
char* findchar(char* str, char c)
{
char* position = NULL;
int i = 0;
for(i = 0; str[i]!='\0';i++)
{
if(str[i] == c)
{
position = &str[i];
break;
}
}
return position;
}
So far it works. However, when i looked at the prototype of strchr():
char *strchr(const char *str, int c);
The second parameter is an int? I'm curious to know.. Why not a char? Does this mean that we can use int for storing characters just like we use a char?
Which brings me to the second question, i tried to change my function to accept an int as a second parameter... but i'm not sure if it's correct and safe to do the following:
char* findchar(char* str, int c)
{
char* position = NULL;
int i = 0;
for(i = 0; str[i]!='\0';i++)
{
if(str[i] == c) //Specifically, is this line correct? Can we test an int against a char?
{
position = &str[i];
break;
}
}
return position;
}
Before ANSI C89, functions were declared without prototypes. The declaration for strchr looked like this back then:
char *strchr();
That's it. No parameters are declared at all. Instead, there were these simple rules:
all pointers are passed as parameters as-is
all integer values of a smaller range than int are converted to int
all floating point values are converted to double
So when you called strchr, what really happened was:
strchr(str, (int)chr);
When ANSI C89 was introduced, it had to maintain backwards compatibility. Therefore it defined the prototype of strchr as:
char *strchr(const char *str, int chr);
This preserves the exact behavior of the above sample call, including the conversion to int. This is important since an implementation may define that passing a char argument works differently than passing an int argument, which makes sense on 8 bit platforms.
Consider the return value of fgetc(), values in the range of unsigned char and EOF, some negative value. This is the kind of value to pass to strchr().
#Roland Illig presents a very good explanation of the history that led to retaining use of int ch with strchr().
OP's code fails/has trouble as follows.
1) char* str is treated like unsigned char *str per §7.23.1.1 3
For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char
2) i should be type size_t, to handle the entire range of the character array.
3) For the purpose of strchr(), the null character is considered part of the search.
The terminating null character is considered to be part of the string.
4) Better to use const as str is not changed.
char* findchar(const char* str, int c) {
const char* position = NULL;
size_t i = 0;
for(i = 0; ;i++) {
if((unsigned char) str[i] == c) {
position = &str[i];
break;
}
if (str[i]=='\0') break;
}
return (char *) position;
}
Further detail
The strchr function locates the first occurrence of c (converted to a char) in the string pointed to by s. C11dr §7.23.5.2 2
So int c is treat like a char. This could imply
if((unsigned char) str[i] == (char) c) {
Yet what I think this is meant:
if((unsigned char) str[i] == (unsigned char)(char) c) {
or simply
if((unsigned char) str[i] == (unsigned char)c) {
I have this code in C but I don't know why I get an error like Argument of type “char” is incompatible with parameter of type const char*
char number_string[size] = { NULL };
int counter = 0;
for (counter = 0; counter < strlen(input_string - 1); counter++)
{
temp = input_string[counter];
if (isdigit(temp))
{
strcat(number_string, temp); //temp variable has the error only in this line
}
}
You're mixing char and char *. The declaration of number_string[] might not warn you, because NULL may simply be defined as 0, which is legal as a char and as a pointer. But the variable temp is definately a problem: You don't show its declaration, but its first assignment makes it a char, and its second use in strcat assumes it is a char *.
If you want to add a single character at a time to a string, you'll have to do it by hand, something like this:
int nslen = strlen(number_string);
for ...
number_string[nslen++] = temp
number_string[nslen] = '\0';
char *strcat(char *dest, const char *src); expects its last argument to have type const char *
temp is a variable of type char.
So you're getting the error Argument of type “char” is incompatible with parameter of type const char*.
You could try something like this...
strncat(number_string, &temp, 1)
I usually favor something like...
sprintf (buffer, "%s%c", number_string, temp)
This is not an answer to your problem with types; it is a suggestion to tackle the problem in another way.
You want to fill a string with all numeric digits from the input string. There is no standard function to do this.
You could use strcat, but that function operates on strings, which must be zero-terminated. You could create a temporary string of two chars – one digit and one null-terminator – but that would be ineffective. strcat also requires you to ensure that you don't overflow the char buffer that you append to.
In cases like yours, it is usually easier to tackle the problem on a low level, where you create the char array yourself, one character at a time. For example, you can iterate through the input string with i and copy all digits to the number string by means of a second index, j:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main()
{
size_t size = 12;
char number[size];
const char *input = "Alpha 123/Bravo 456/Charlie 789/Delta 007";
int i, j;
j = 0;
for (i = 0; input[i] != '\0'; i++) {
if (isdigit((unsigned char) input[i]) && j + 1 < size) {
number[j++] = input[i];
}
}
number[j] = '\0';
puts(number);
return 0;
}
Note how the code keeps track of the characters in the number string and how it takes care not to overflow the buffer. The number string may be truncated, but it will always be null-terminated.
I've also used input[i] != '\0' to detect the end of the string (which is, by definition, the null terminator '\0') instead of calling strlen(input), which always starts looking for the terminator fro the beginning of the string.
There are multiple problems in your code:
calling strlen(input_string - 1) most likely invokes undefined behaviour. You probably meant strlen(input_string) - 1 which would still cause the loop to run too far if input_string is an empty string;
calling strlen for each iteration is inefficient anyway;
calling isdigit(temp) is incorrect if temp is a char and char is signed by default;
strcat cannot be used the way you call it;
you should check for potential buffer overflow if all digits do not fit in the destination array.
Here is a much simpler function to extract all digits from the input_string:
char number_string[size];
int i, j;
for (i = j = 0; input_string[i] != '\0'; i++) {
unsigned char uc = input_string[i];
if (j < size - 1 && isdigit(uc)) {
number_string[j++] = uc;
}
}
number_string[j] = '\0';
I have a character pointer that points the begining of a string and an index less than the length of the string. Now I want to create a pointer to point a substring of original string from the begining to the index or a substring with above constraints. Please help me to find a way to get it done. Here is a bit of the code:
char* ch="323+465";//this is the original string
int index=2; //this is the index upto which I wish to create a substring,
// in java, it would have been ch.substring(0,3), if ch were a String
Thanks in advance.
You can't do that without creating 3 strings. The char point only marks the beginning of the string, so you would need to combine a pointer and an index into a new type. Remember you don't have strings in C. In languages like Java (and others) will create copies of the sub string anyway.
struct pseudo_string { char *s, int index; } vstring[3];
char* ch="323+465";
vstring[0].s = ch;
vstring[0].index = 2;
vstring[1].s = ch + index + 1; // weird
vstring[1].index = 1;
vstring[2].s = vstring[1].s + 1;
vstring[2].index = 2;
So it is overly complex and useless. In this case index is being used as counter...
If you want to keep the same base pointer, you gonna need 2 indices or 1 index and a len:
struct pseudo_string2 { char *s; int start; int end; };
But that's an overkill for small strings.
If don't want to use malloc, you can try to use a matrix:
char vstring[3][10]={0};
strncpy(vstring[0], ch, 3);
strncpy(vstring[1], ch+3, 1);
strncpy(vstring[2], ch+4, 3);
The advantage of the matrix, even if you waste few bytes, is that you don't need to deallocate it. But if you need to use these values outside this function, than you don't have another scape than to use malloc and free (don't consider globals for that ;-).
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * substr(char *s, int start, int end)
{
int size = end - start + 2; // 1 for the inclusive limits and another 1 for the \0
char * r = (char*)malloc(size);
strncpy(r,s+start, size-1);
r[size-1]=0;
return r;
}
int main()
{
char* ch="323+465";
char *parts[3];
parts[0] = substr(ch, 0,2);
parts[1] = substr(ch, 3,3);
parts[2] = substr(ch, 4,6);
printf("%s %s %s\n", parts[0], parts[1], parts[2]);
free(parts[0]);
free(parts[1]);
free(parts[2]);
}
Make a copy of a suitable number of characters:
char * substr = malloc(index + 2);
strncpy(substr, ch, index + 1);
substr[index + 1] = 0;
// ...
free(substr);
If you're happy to mutilate the original string, just insert a null byte:
ch[index + 1] = 0;
The odd + 1 comes from the fact that your index seems to be inclusive, which is generally a bad idea.
You can't, because that would imply modifying the string literal, which is illegal.
Alternative:
char ch[]="323+465";
int index=2;
ch[index] = '\0';
First, make the string writable by using a char array instead of a pointer to a string literal. Then
char ch[] = "323+456";
int idx = 2;
ch[idx] = 0; /* Now ch holds the string "32" */
You should avoid an identifier clash with the classic BSD index function, that's why I used idx instead.
This solution assumes it is okay to modify the original string. If not, you need to allocate a new string first.
the same behavior of java substring (allocates new string)
char* res = (char*)malloc(index+2);
strncpy(res,ch,index+1);
res[index+1]='\0';
I see that you try to delimit by +, so easier is to use strtok :
char ch[] ="323+465";
char * res;
res = strtok (ch,"+");
// res= 323
I am tring to create a sub-routine that inserts a string into another string. I want to check that the host string is going to have enough capacity to hold all the characters and if not return an error integer. This requires using something like sizeof but that can be called using a pointer. My code is below and I would be very gateful for any help.
#include<stdio.h>
#include<conio.h>
//#include "string.h"
int string_into_string(char* host_string, char* guest_string, int insertion_point);
int main(void) {
char string_one[21] = "Hello mother"; //12 characters
char string_two[21] = "dearest "; //8 characters
int c;
c = string_into_string(string_one, string_two, 6);
printf("Sub-routine string_into_string returned %d and creates the string: %s\n", c, string_one);
getch();
return 0;
}
int string_into_string(char* host_string, char* guest_string, int insertion_point) {
int i, starting_length_of_host_string;
//check host_string is long enough
if(strlen(host_string) + strlen(guest_string) >= sizeof(host_string) + 1) {
//host_string is too short
sprintf(host_string, "String too short(%d)!", sizeof(host_string));
return -1;
}
starting_length_of_host_string = strlen(host_string);
for(i = starting_length_of_host_string; i >= insertion_point; i--) { //make room
host_string[i + strlen(guest_string)] = host_string[i];
}
//i++;
//host_string[i] = '\0';
for(i = 1; i <= strlen(guest_string); i++) { //insert
host_string[i + insertion_point - 1] = guest_string[i - 1];
}
i = strlen(guest_string) + starting_length_of_host_string;
host_string[i] = '\0';
return strlen(host_string);
}
C does not allow you to pass arrays as function arguments, so all arrays of type T[N] decay to pointers of type T*. You must pass the size information manually. However, you can use sizeof at the call site to determine the size of an array:
int string_into_string(char * dst, size_t dstlen, char const * src, size_t srclen, size_t offset, size_t len);
char string_one[21] = "Hello mother";
char string_two[21] = "dearest ";
string_into_string(string_one, sizeof string_one, // gives 21
string_two, strlen(string_two), // gives 8
6, strlen(string_two));
If you are creating dynamic arrays with malloc, you have to store the size information somewhere separately anyway, so this idiom will still fit.
(Beware that sizeof(T[N]) == N * sizeof(T), and I've used the fact that sizeof(char) == 1 to simplify the code.)
This code needs a whole lot more error handling but should do what you need without needing any obscure loops. To speed it up, you could also pass the size of the source string as parameter, so the function does not need to calculate it in runtime.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
signed int string_into_string (char* dest_buf,
int dest_size,
const char* source_str,
int insert_index)
{
int source_str_size;
char* dest_buf_backup;
if (insert_index >= dest_size) // sanity check of parameters
{
return -1;
}
// save data from the original buffer into temporary backup buffer
dest_buf_backup = malloc (dest_size - insert_index);
memcpy (dest_buf_backup,
&dest_buf[insert_index],
dest_size - insert_index);
source_str_size = strlen(source_str);
// copy new data into the destination buffer
strncpy (&dest_buf[insert_index],
source_str,
source_str_size);
// restore old data at the end
strcpy(&dest_buf[insert_index + source_str_size],
dest_buf_backup);
// delete temporary buffer
free(dest_buf_backup);
}
int main()
{
char string_one[21] = "Hello mother"; //12 characters
char string_two[21] = "dearest "; //8 characters
(void) string_into_string (string_one,
sizeof(string_one),
string_two,
6);
puts(string_one);
return 0;
}
I tried using a macro and changing string_into_string to include the requirement for a size argument, but I still strike out when I call the function from within another function. I tried using the following Macro:
#define STRING_INTO_STRING( a, b, c) (string_into_string2(a, sizeof(a), b, c))
The other function which causes failure is below. This fails because string has already become the pointer and therefore has size 4:
int string_replace(char* string, char* string_remove, char* string_add) {
int start_point;
int c;
start_point = string_find_and_remove(string, string_remove);
if(start_point < 0) {
printf("string not found: %s\n ABORTING!\n", string_remove);
while(1);
}
c = STRING_INTO_STRING(string, string_add, start_point);
return c;
}
Looks like this function will have to proceed at risk. looking at strcat it also proceeds at risk, in that it doesn't check that the string you are appending to is large enough to hold its intended contents (perhaps for the very same reason).
Thanks for everyone's help.