User-defined function similar to squeeze() - c

So I'm trying to do the practice exercises in K&R. It wants me to make a function similar to squeeze, I don't get whats wrong with it. I desk checked it already. I don't want a solution found on the net, I wanna understand why my code wont work.
//removes characters that are present in both strings
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define MAXLTR 15
void removesame(char s1[],char s2[]);
int main(void)
{
char string1[MAXLTR],string2[MAXLTR];
printf("Enter a string: ");
scanf("\n%s",&string1);
printf("\nEnter the letters/words to be removed: ");
scanf("\n%s",&string2);
removesame(string1,string2);
printf("\nFinal output: %s",string1);
getch();
}
void removesame(char s1[],char s2[])
{
char temp[MAXLTR];
int arraycntr,comparecntr;
for(comparecntr = 0; comparecntr < MAXLTR; comparecntr++)
{
for(arraycntr = 0;arraycntr < MAXLTR;arraycntr++)
{
if(s1[arraycntr] == s2[arraycntr])
s1[arraycntr] == '\t';
}
}
comparecntr = 0;
for(arraycntr = 0; arraycntr < MAXLTR; arraycntr++)
{
if(s1[arraycntr] != '\t')
{
temp[comparecntr] = s1[arraycntr];
++comparecntr;
}
}
for(arraycntr = 0; arraycntr < MAXLTR; arraycntr++)
s1[arraycntr] = '\0';
for(arraycntr = 0;arraycntr < MAXLTR; arraycntr++)
s1[arraycntr] = temp[arraycntr];
}

This is not an assignment, but is an equality test:
s1[arraycntr] == '\t';
you meant:
s1[arraycntr] = '\t';
If you compile with a high warning level, the compiler may emit a message alerting you to this. The Microsoft VC compiler emits the following warning:
C:\devel\cpp\stackoverflow\main.c(32) : warning C4553: '==' : operator has no effect; did you intend '='?
The initial for loops only check if s1 and s2 have the same values in the same indexes, it does not check if a char in s1 exists anywhere in s2. The terminating conditions on the for loops should also be the lengths of s1 and s2, not MAXLTR:
size_t arraycntr,comparecntr;
for(comparecntr = 0; comparecntr < strlen(s2); comparecntr++)
{
for(arraycntr = 0;arraycntr < strlen(s1) ;arraycntr++)
{
if(s1[arraycntr] == s2[comparecntr])
s1[arraycntr] = `\t`;
}
}
The next for loop should use strlen(s1) also and just assign null terminator to temp after:
comparecntr = 0;
for(arraycntr = 0; arraycntr < strlen(s1); arraycntr++)
{
if(s1[arraycntr] != `\t`)
{
temp[comparecntr] = s1[arraycntr];
++comparecntr;
}
}
temp[comparecntr] = '\0';
temp is not initialised anywhere, so contains random data, apart from that just entered during this for. Without a null terminator in temp, s1 will end with no null terminator also (you will probably see garbage printed afterwards). Finally, just strlen(temp) + 1 when populating s1:
for(arraycntr = 0;arraycntr < strlen(temp) + 1; arraycntr++)
s1[arraycntr] = temp[arraycntr];
The + 1 will copy the null terminator to s1.
Minor note, instead of calling strlen() in the terminating condition of the for loops you can store this instead:
size_t chars_to_copy;
for(arraycntr = 0, chars_to_copy = strlen(temp) + 1;
arraycntr < chars_to_copy;
arraycntr++)
{
s1[arraycntr] = temp[arraycntr];
}

Related

Why does this code bit work in clion but doesn't in VS

Hi I copied the following code from my linux machine with clion running. But in VS on Windows it seems to cause problems
entry_t* find_entry( char* n )
{
// TODO (2)
int x = strlen(n);
char str[x];
for (size_t i = 0; i < strlen(str); i++)
{
str[i] = toupper(n[i]);
}
n = &str;
for (size_t i = 0; i < list_length; i++)
{
if (strcmp(n, name_list[i].name) == 0)
{
return &name_list[i];
}
}
}
VS underlines the x in char str[x]; before the statement do find x was in the brackets of str. I thought finding the length first in another variable would solve the problem
VS give the following error
Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
Fehler (aktiv) E0028 Der Ausdruck muss einen Konstantenwert aufweisen. Names.exe - x64-Debug C:\Users\Eyüp\source\repos\09\main.c 102
trying my best to translate it
-> Error(active) E0028 Statement needs to be a constant value
Variable-length arrays (i.e. arrays whose size is not known at compile-time) are not supported in MSVC because they don't care. Hence you need to use malloc and friends instead.
However that is not the only problem in your code: it has multiple undefined behaviours. Here is a suggested fix:
entry_t* find_entry( char* n )
{
// return value of strlen is of type size_t, not int
size_t x = strlen(n);
// [x] was wrong, it needs to be [x + 1] for the null terminator!
char *str = malloc(x + 1);
// do not use strlen again in the loop. In worst case it does need
// to go through the entire string looking for the null terminator every time.
// You must copy the null terminator, hence i <= x or i < x + 1
for (size_t i = 0; i <= x; i++)
{
// the argument of toupper needs to be *unsigned char*
str[i] = toupper((unsigned char)n[i]);
}
// why this did even exist? And it has type error anyway
// n = &str;
for (size_t i = 0; i < list_length; i++)
{
if (strcmp(str, name_list[i].name) == 0)
{
// need to free the str...
free(str);
return &name_list[i];
}
}
// in both paths...
free(str);
// add default return value
return NULL;
}
Your code invokes undefined behaviour:
as you do not null terminate the string
you call strlen on not null terminated (and initially not initialized string)
The logic is also wrong.
entry_t* find_entry( const char* n )
{
// TODO (2)
size_t x = strlen(n);
char str[x + 1];
for (size_t i = 0; i <= x; i++)
{
str[i] = toupper((unsigned char)n[i]);
}
str[x] = 0;
for (size_t i = 0; i < list_length; i++)
{
if (strcmp(str, name_list[i].name) == 0)
{
return &name_list[i];
}
}
return NULL;
}
You need to return something if the sting was not found.
To use VLAs in VS How to use Visual Studio as an IDE with variable length array(VLA) working?

Replacing doubles quotes with escape character double quotes in a C string

From a vector, resources (strings) are fetched and some operations are done on the strings later. Out of these, one of the strings (resource) has the following value:
"bundle 20B / / <a href="http://ezproxy.rit.edu/login?url=http://firstsearch.oclc.org/fsip?dbname=CWI&done=refer 1234567890123456789012345678901234567890123456789012345678901234567890123456 "
But, as this string has a (") double quotes character in it (without \ before it; it is being treated as the end of the string and thus leading to an error at further stage. I want to write a code wherein, if double quotes are detected in the received value from vector, it should be replaced with (\"), so that this error doesn't take place and this process should take place for all the occurrences of double quotes character in that string.
I feel that my solution is not that good. Please suggest some changes in this code to get a better and optimized solution.
for(i = 0; i < vector_getCount(resNames); i++)
{
char *resource = vector_elementAt(resNames, i);
int size = 0;
char str[100];
int i = 0;
while(resource[size] != '\0')
{
if(resource[size] == '\"')
{
str[i] = '\\';
str[i+1] = '\"';
i = i+2;
}
else
{
str[i++] = resource[size];
}
size++;
}
vector_setElementAt(resNames, i, str)
}
In this code, this lengthy process will take place for all vector elements. But this would be very bad for performance. Is there a better solution?
I've tried to write a simple function making a specified character to do escape. You can toy with it.
#include <stdio.h>
#include <string.h>
#include <assert.h>
void myEscaper(char const * str_in,
char * str_out,
size_t str_out_size,
char escape_char) {
size_t length_str = strlen(str_in);
assert(length_str < str_out_size);
size_t j = 0;
for (size_t i = 0; i < length_str; ++i) {
if (str_in[i] == escape_char) {
str_out[j] = '\\';
str_out[++j] = str_in[i];
}
else {
str_out[j] = str_in[i];
}
++j;
}
str_out[j] = 0;
}
int main() {
char const * in = "acdavca";
char out[100] = {0};
myEscaper(in, out, sizeof(out), 'a');
// You can call the below one
//myEscaper(in, out, sizeof(out), '\"');
puts(out);
}

Attempting to split and store arrays similar to strtok

For an assignment in class, we have been instructed to write a program which takes a string and a delimiter and then takes "words" and stores them in a new array of strings. i.e., the input ("my name is", " ") would return an array with elements "my" "name" "is".
Roughly, what I've attempted is to:
Use a separate helper called number_of_delimeters() to determine the size of the array of strings
Iterate through the initial array to find the number of elements in a given string which would be placed in the array
Allocate storage within my array for each string
Store the elements within the allocated memory
Include directives:
#include <stdlib.h>
#include <stdio.h>
This is the separate helper:
int number_of_delimiters (char* s, int d)
{
int numdelim = 0;
for (int i = 0; s[i] != '\0'; i++)
{
if (s[i] == d)
{
numdelim++;
}
}
return numdelim;
}
`This is the function itself:
char** split_at (char* s, char d)
{
int numdelim = number_of_delimiters(s, d);
int a = 0;
int b = 0;
char** final = (char**)malloc((numdelim+1) * sizeof(char*));
for (int i = 0; i <= numdelim; i++)
{
int sizeofj = 0;
while (s[a] != d)
{
sizeofj++;
a++;
}
final[i] = (char*)malloc(sizeofj);
a++;
int j = 0;
while (j < sizeofj)
{
final[i][j] = s[b];
j++;
b++;
}
b++;
final[i][j+1] = '\0';
}
return final;
}
To print:
void print_string_array(char* a[], unsigned int alen)
{
printf("{");
for (int i = 0; i < alen; i++)
{
if (i == alen - 1)
{
printf("%s", a[i]);
}
else
{
printf("%s ", a[i]);
}
}
printf("}");
}
int main(int argc, char *argv[])
{
print_string_array(split_at("Hi, my name is none.", ' '), 5);
return 0;
}
This currently returns {Hi, my name is none.}
After doing some research, I realized that the purpose of this function is either similar or identical to strtok. However, looking at the source code for this proved to be little help because it included concepts we have not yet used in class.
I know the question is vague, and the code rough to read, but what can you point to as immediately problematic with this approach to the problem?
The program has several problems.
while (s[a] != d) is wrong, there is no delimiter after the last word in the string.
final[i][j+1] = '\0'; is wrong, j+1 is one position too much.
The returned array is unusable, unless you know beforehand how many elements are there.
Just for explanation:
strtok will modify the array you pass in! After
char test[] = "a b c ";
for(char* t = test; strtok(t, " "); t = NULL);
test content will be:
{ 'a', 0, 'b', 0, 'c', 0, 0 }
You get subsequently these pointers to your test array: test + 0, test + 2, test + 4, NULL.
strtok remembers the pointer you pass to it internally (most likely, you saw a static variable in your source code...) so you can (and must) pass NULL the next time you call it (as long as you want to operate on the same source string).
You, in contrast, apparently want to copy the data. Fine, one can do so. But here we get a problem:
char** final = //...
return final;
void print_string_array(char* a[], unsigned int alen)
You just return the array, but you are losing length information!
How do you want to pass the length to your print function then?
char** tokens = split_at(...);
print_string_array(tokens, sizeof(tokens));
will fail, because sizeof(tokens) will always return the size of a pointer on your local system (most likely 8, possibly 4 on older hardware)!
My personal recommendation: create a null terminated array of c strings:
char** final = (char**)malloc((numdelim + 2) * sizeof(char*));
// ^ (!)
// ...
final[numdelim + 1] = NULL;
Then your print function could look like this:
void print_string_array(char* a[]) // no len parameter any more!
{
printf("{");
if(*a)
{
printf("%s", *a); // printing first element without space
for (++a; *a; ++a) // *a: checking, if current pointer is not NULL
{
printf(" %s", *a); // next elements with spaces
}
}
printf("}");
}
No problems with length any more. Actually, this is exactly the same principle C strings use themselves (the terminating null character, remember?).
Additionally, here is a problem in your own code:
while (j < sizeofj)
{
final[i][j] = s[b];
j++; // j will always point behind your string!
b++;
}
b++;
// thus, you need:
final[i][j] = '\0'; // no +1 !
For completeness (this was discovered by n.m. already, see the other answer): If there is no trailing delimiter in your source string,
while (s[a] != d)
will read beyond your input string (which is undefined behaviour and could result in your program crashing). You need to check for the terminating null character, too:
while(s[a] && s[a] != d)
Finally: how do you want to handle subsequent delimiters? Currently, you will insert empty strings into your array? Print out your strings as follows (with two delimiting symbols - I used * and + like birth and death...):
printf("*%s+", *a);
and you will see. Is this intended?
Edit 2: The variant with pointer arithmetic (only):
char** split_at (char* s, char d)
{
int numdelim = 0;
char* t = s; // need a copy
while(*t)
{
numdelim += *t == d;
++t;
}
char** final = (char**)malloc((numdelim + 2) * sizeof(char*));
char** f = final; // pointer to current position within final
t = s; // re-assign t, using s as start pointer for new strings
while(*t) // see above
{
if(*t == d) // delimiter found!
{
// can subtract pointers --
// as long as they point to the same array!!!
char* n = (char*)malloc(t - s + 1); // +1: terminating null
*f++ = n; // store in position pointer and increment it
while(s != t) // copy the string from start to current t
*n++ = *s++;
*n = 0; // terminate the new string
}
++t; // next character...
}
*f = NULL; // and finally terminate the string array
return final;
}
While I've now been shown a more elegant solution, I've found and rectified the issues in my code:
char** split_at (char* s, char d)
{
int numdelim = 0;
int x;
for (x = 0; s[x] != '\0'; x++)
{
if (s[x] == d)
{
numdelim++;
}
}
int a = 0;
int b = 0;
char** final = (char**)malloc((numdelim+1) * sizeof(char*));
for (int i = 0; i <= numdelim; i++)
{
int sizeofj = 0;
while ((s[a] != d) && (a < x))
{
sizeofj++;
a++;
}
final[i] = (char*)malloc(sizeofj);
a++;
int j = 0;
while (j < sizeofj)
{
final[i][j] = s[b];
j++;
b++;
}
final[i][j] = '\0';
b++;
}
return final;
}
I consolidated what I previously had as a helper function, and modified some points where I incorrectly incremented .

Access to a specific element on an array using pointers C

I am working to replace '.' by '::' in a phrase.
I am given a phrase with 30 caracteres, without using other array. I would like to access to the last element using a pointer.
However,
First I count the dots in my phrase.
actualSize= 0; i= 0; dotNumb= 0;
while (i<actualSize){
if (tab[i]=='.') dotNumb++
i++
}
Now I should start by the end; whenever I find an element I move it, whenever I find a '.' I make an operation two times, by copying ':' two times.
Now I need to access to this element tab[dotNumb+actualSize]
Can I do it this way, or should I use pointers .
int newSize = dotNumb+actualSize ;
int j=newSize ;
int cursor=actualSize;
while (j>0){
if (tab[i]!='.') {tab[j]=tab[cursor]; }
else{tab[j]=':';tab[--j]=':';}
cursor--; j--;
}
The code you wrote does not work, (you made some typos, for example actualSize is set to 0 so the loop counting the dots will never execute. and the logic of your code would delete the character preceding a dot in the original array). You probably want something like that:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *tab = malloc(30 + 1);
if (tab == NULL) {
return -1;
}
strcpy(tab, "123456789.123456789.123456789.");
printf("Original string: %s\n", tab);
size_t actualSize = strlen(tab);
int dotNumb = 0;
for (int i = 0; i < actualSize; i++) {
if (tab[i]=='.') {
dotNumb++;
}
}
const size_t newSize = dotNumb + actualSize + 1;
tab = realloc (tab, newSize);
if (tab == NULL) {
/* leak memory previously allocated in tab */
return -1;
}
tab[newSize] = '\0'; /* termination character */
int j = newSize ;
for (size_t cursor = actualSize; cursor > 0; cursor--) {
if (tab[cursor] != '.') {
tab[j--] = tab[cursor];
}
else {
tab[j--] = ':';
tab[j--] = ':';
}
}
printf("Modified string: %s\n", tab);
return 0;
}
You can test the code here:
http://ideone.com/YOxJKo

remove a specified number of characters from a string in C

I can't write a workable code for a function that deletes N characters from the string S, starting from position P. How you guys would you write such a function?
void remove_substring(char *s, int p, int n) {
int i;
if(n == 0) {
printf("%s", s);
}
for (i = 0; i < p - 1; i++) {
printf("%c", s[i]);
}
for (i = strlen(s) - n; i < strlen(s); i++) {
printf("%c", s[i]);
}
}
Example:
s: "abcdefghi"
p: 4
n: 3
output:
abcghi
But for a case like n = 0 and p = 1 it's not working!
Thanks a lot!
A few people have shown you how to do this, but most of their solutions are highly condensed, use standard library functions or simply don't explain what's going on. Here's a version that includes not only some very basic error checking but some explanation of what's happening:
void remove_substr(char *s, size_t p, size_t n)
{
// p is 1-indexed for some reason... adjust it.
p--;
// ensure that we're not being asked to access
// memory past the current end of the string.
// Note that if p is already past the end of
// string then p + n will, necessarily, also be
// past the end of the string so this one check
// is sufficient.
if(p + n >= strlen(s))
return;
// Offset n to account for the data we will be
// skipping.
n += p;
// We copy one character at a time until we
// find the end-of-string character
while(s[n] != 0)
s[p++] = s[n++];
// And make sure our string is properly terminated.
s[p] = 0;
}
One caveat to watch out for: please don't call this function like this:
remove_substr("abcdefghi", 4, 3);
Or like this:
char *s = "abcdefghi";
remove_substr(s, 4, 3);
Doing so will result in undefined behavior, as string literals are read-only and modifying them is not allowed by the standard.
Strictly speaking, you didn't implement a removal of a substring: your code prints the original string with a range of characters removed.
Another thing to note is that according to your example, the index p is one-based, not zero-based like it is in C. Otherwise the output for "abcdefghi", 4, 3 would have been "abcdhi", not "abcghi".
With this in mind, let's make some changes. First, your math is a little off: the last loop should look like this:
for (i = p+n-1; i < strlen(s); i++) {
printf("%c", s[i]);
}
Demo on ideone.
If you would like to use C's zero-based indexing scheme, change your loops as follows:
for (i = 0; i < p; i++) {
printf("%c", s[i]);
}
for (i = p+n; i < strlen(s); i++) {
printf("%c", s[i]);
}
In addition, you should return from the if at the top, or add an else:
if(n == 0) {
printf("%s", s);
return;
}
or
if(n == 0) {
printf("%s", s);
} else {
// The rest of your code here
...
}
or remove the if altogether: it's only an optimization, your code is going to work fine without it, too.
Currently, you code would print the original string twice when n is 0.
If you would like to make your code remove the substring and return a result, you need to allocate the result, and replace printing with copying, like this:
char *remove_substring(char *s, int p, int n) {
// You need to do some checking before calling malloc
if (n == 0) return s;
size_t len = strlen(s);
if (n < 0 || p < 0 || p+n > len) return NULL;
size_t rlen = len-n+1;
char *res = malloc(rlen);
if (res == NULL) return NULL;
char *pt = res;
// Now let's use the two familiar loops,
// except printf("%c"...) will be replaced with *p++ = ...
for (int i = 0; i < p; i++) {
*pt++ = s[i];
}
for (int i = p+n; i < strlen(s); i++) {
*pt++ = s[i];
}
*pt='\0';
return res;
}
Note that this new version of your code returns dynamically allocated memory, which needs to be freed after use.
Here is a demo of this modified version on ideone.
Try copying the first part of the string, then the second
char result[10];
const char input[] = "abcdefg";
int n = 3;
int p = 4;
strncpy(result, input, p);
strncpy(result+p, input+p+n, length(input)-p-n);
printf("%s", result);
If you are looking to do this without the use of functions like strcpy or strncpy (which I see you said in a comment) then use a similar approach to how strcpy (or at least one possible variant) works under the hood:
void strnewcpy(char *dest, char *origin, int n, int p) {
while(p-- && *dest++ = *origin++)
;
origin += n;
while(*dest++ = *origin++)
;
}
metacode:
allocate a buffer for the destination
decalre a pointer s to your source string
advance the pointer "p-1" positions in your source string and copy them on the fly to destination
advance "n" positions
copy rest to destination
What did you try? Doesn't strcpy(s+p, s+p+n) work?
Edit: Fixed to not rely on undefined behaviour in strcpy:
void remove_substring(char *s, int p, int n)
{
p--; // 1 indexed - why?
memmove(s+p, s+p+n, strlen(s) - n);
}
If your heart's really set on it, you can also replace the memmove call with a loop:
char *dst = s + p;
char *src = s + p + n;
for (int i = 0; i < strlen(s) - n; i++)
*dst++ = *src++;
And if you do that, you can strip out the strlen call, too:
while ((*dst++ = *src++) != '\0);
But I'm not sure I recommend compressing it that much.

Resources